1/*
2 * Copyright © 2016 Intel Corporation
3 *
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 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
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
13 * Software.
14 *
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 NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24#include <assert.h>
25#include <stdbool.h>
26
27#include "radv_meta.h"
28#include "radv_private.h"
29#include "nir/nir_builder.h"
30#include "sid.h"
31/**
32 * Vertex attributes used by all pipelines.
33 */
34struct vertex_attrs {
35	float position[2]; /**< 3DPRIM_RECTLIST */
36};
37
38/* passthrough vertex shader */
39static nir_shader *
40build_nir_vs(void)
41{
42	const struct glsl_type *vec4 = glsl_vec4_type();
43
44	nir_builder b;
45	nir_variable *a_position;
46	nir_variable *v_position;
47
48	nir_builder_init_simple_shader(&b, NULL, MESA_SHADER_VERTEX, NULL);
49	b.shader->info->name = ralloc_strdup(b.shader, "meta_resolve_vs");
50
51	a_position = nir_variable_create(b.shader, nir_var_shader_in, vec4,
52					 "a_position");
53	a_position->data.location = VERT_ATTRIB_GENERIC0;
54
55	v_position = nir_variable_create(b.shader, nir_var_shader_out, vec4,
56					 "gl_Position");
57	v_position->data.location = VARYING_SLOT_POS;
58
59	nir_copy_var(&b, v_position, a_position);
60
61	return b.shader;
62}
63
64/* simple passthrough shader */
65static nir_shader *
66build_nir_fs(void)
67{
68	const struct glsl_type *vec4 = glsl_vec4_type();
69	nir_builder b;
70	nir_variable *f_color; /* vec4, fragment output color */
71
72	nir_builder_init_simple_shader(&b, NULL, MESA_SHADER_FRAGMENT, NULL);
73	b.shader->info->name = ralloc_asprintf(b.shader,
74					       "meta_resolve_fs");
75
76	f_color = nir_variable_create(b.shader, nir_var_shader_out, vec4,
77				      "f_color");
78	f_color->data.location = FRAG_RESULT_DATA0;
79	nir_store_var(&b, f_color, nir_imm_vec4(&b, 0.0, 0.0, 0.0, 1.0), 0xf);
80
81	return b.shader;
82}
83
84static VkResult
85create_pass(struct radv_device *device)
86{
87	VkResult result;
88	VkDevice device_h = radv_device_to_handle(device);
89	const VkAllocationCallbacks *alloc = &device->meta_state.alloc;
90	VkAttachmentDescription attachments[2];
91	int i;
92
93	for (i = 0; i < 2; i++) {
94		attachments[i].format = VK_FORMAT_UNDEFINED;
95		attachments[i].samples = 1;
96		attachments[i].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
97		attachments[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
98		attachments[i].initialLayout = VK_IMAGE_LAYOUT_GENERAL;
99		attachments[i].finalLayout = VK_IMAGE_LAYOUT_GENERAL;
100	}
101
102	result = radv_CreateRenderPass(device_h,
103				       &(VkRenderPassCreateInfo) {
104					       .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
105						       .attachmentCount = 2,
106						       .pAttachments = attachments,
107						       .subpassCount = 1,
108								.pSubpasses = &(VkSubpassDescription) {
109						       .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
110						       .inputAttachmentCount = 0,
111						       .colorAttachmentCount = 2,
112						       .pColorAttachments = (VkAttachmentReference[]) {
113							       {
114								       .attachment = 0,
115								       .layout = VK_IMAGE_LAYOUT_GENERAL,
116							       },
117							       {
118								       .attachment = 1,
119								       .layout = VK_IMAGE_LAYOUT_GENERAL,
120							       },
121						       },
122						       .pResolveAttachments = NULL,
123						       .pDepthStencilAttachment = &(VkAttachmentReference) {
124							       .attachment = VK_ATTACHMENT_UNUSED,
125						       },
126						       .preserveAttachmentCount = 0,
127						       .pPreserveAttachments = NULL,
128					       },
129								.dependencyCount = 0,
130									 },
131				       alloc,
132				       &device->meta_state.resolve.pass);
133
134	return result;
135}
136
137static VkResult
138create_pipeline(struct radv_device *device,
139                VkShaderModule vs_module_h)
140{
141	VkResult result;
142	VkDevice device_h = radv_device_to_handle(device);
143
144	struct radv_shader_module fs_module = {
145		.nir = build_nir_fs(),
146	};
147
148	if (!fs_module.nir) {
149		/* XXX: Need more accurate error */
150		result = VK_ERROR_OUT_OF_HOST_MEMORY;
151		goto cleanup;
152	}
153
154	result = radv_graphics_pipeline_create(device_h,
155					       radv_pipeline_cache_to_handle(&device->meta_state.cache),
156					       &(VkGraphicsPipelineCreateInfo) {
157						       .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
158						       .stageCount = 2,
159						       .pStages = (VkPipelineShaderStageCreateInfo[]) {
160						       {
161							       .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
162							       .stage = VK_SHADER_STAGE_VERTEX_BIT,
163							       .module = vs_module_h,
164							       .pName = "main",
165						       },
166						       {
167							       .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
168							       .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
169							       .module = radv_shader_module_to_handle(&fs_module),
170							       .pName = "main",
171						       },
172					       },
173					       .pVertexInputState = &(VkPipelineVertexInputStateCreateInfo) {
174						       .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
175						       .vertexBindingDescriptionCount = 1,
176						       .pVertexBindingDescriptions = (VkVertexInputBindingDescription[]) {
177							       {
178								       .binding = 0,
179								       .stride = sizeof(struct vertex_attrs),
180								       .inputRate = VK_VERTEX_INPUT_RATE_VERTEX
181							       },
182						       },
183						       .vertexAttributeDescriptionCount = 1,
184						       .pVertexAttributeDescriptions = (VkVertexInputAttributeDescription[]) {
185							       {
186								       /* Position */
187								       .location = 0,
188								       .binding = 0,
189								       .format = VK_FORMAT_R32G32_SFLOAT,
190								       .offset = offsetof(struct vertex_attrs, position),
191							       },
192						       },
193					       },
194					       .pInputAssemblyState = &(VkPipelineInputAssemblyStateCreateInfo) {
195						       .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
196						       .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
197						       .primitiveRestartEnable = false,
198					       },
199					       .pViewportState = &(VkPipelineViewportStateCreateInfo) {
200						       .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
201						       .viewportCount = 0,
202						       .scissorCount = 0,
203					       },
204					       .pRasterizationState = &(VkPipelineRasterizationStateCreateInfo) {
205						       .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
206						       .depthClampEnable = false,
207						       .rasterizerDiscardEnable = false,
208						       .polygonMode = VK_POLYGON_MODE_FILL,
209						       .cullMode = VK_CULL_MODE_NONE,
210						       .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
211					       },
212					       .pMultisampleState = &(VkPipelineMultisampleStateCreateInfo) {
213						       .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
214						       .rasterizationSamples = 1,
215						       .sampleShadingEnable = false,
216						       .pSampleMask = NULL,
217						       .alphaToCoverageEnable = false,
218						       .alphaToOneEnable = false,
219					       },
220					       .pColorBlendState = &(VkPipelineColorBlendStateCreateInfo) {
221						       .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
222						       .logicOpEnable = false,
223						       .attachmentCount = 2,
224						       .pAttachments = (VkPipelineColorBlendAttachmentState []) {
225							       {
226							       .colorWriteMask = VK_COLOR_COMPONENT_R_BIT |
227									       VK_COLOR_COMPONENT_G_BIT |
228									       VK_COLOR_COMPONENT_B_BIT |
229									       VK_COLOR_COMPONENT_A_BIT,
230							       },
231							       {
232							       .colorWriteMask = 0,
233
234							       }
235						       },
236						},
237						  .pDynamicState = NULL,
238																       .renderPass = device->meta_state.resolve.pass,
239																       .subpass = 0,
240																       },
241					       &(struct radv_graphics_pipeline_create_info) {
242						       .use_rectlist = true,
243						       .custom_blend_mode = V_028808_CB_RESOLVE,
244							       },
245					       &device->meta_state.alloc,
246					       &device->meta_state.resolve.pipeline);
247	if (result != VK_SUCCESS)
248		goto cleanup;
249
250	goto cleanup;
251
252cleanup:
253	ralloc_free(fs_module.nir);
254	return result;
255}
256
257void
258radv_device_finish_meta_resolve_state(struct radv_device *device)
259{
260	struct radv_meta_state *state = &device->meta_state;
261	VkDevice device_h = radv_device_to_handle(device);
262	VkRenderPass pass_h = device->meta_state.resolve.pass;
263	const VkAllocationCallbacks *alloc = &device->meta_state.alloc;
264
265	if (pass_h)
266		radv_DestroyRenderPass(device_h, pass_h,
267					     &device->meta_state.alloc);
268
269	VkPipeline pipeline_h = state->resolve.pipeline;
270	if (pipeline_h) {
271		radv_DestroyPipeline(device_h, pipeline_h, alloc);
272	}
273}
274
275VkResult
276radv_device_init_meta_resolve_state(struct radv_device *device)
277{
278	VkResult res = VK_SUCCESS;
279
280	zero(device->meta_state.resolve);
281
282	struct radv_shader_module vs_module = { .nir = build_nir_vs() };
283	if (!vs_module.nir) {
284		/* XXX: Need more accurate error */
285		res = VK_ERROR_OUT_OF_HOST_MEMORY;
286		goto fail;
287	}
288
289	res = create_pass(device);
290	if (res != VK_SUCCESS)
291		goto fail;
292
293	VkShaderModule vs_module_h = radv_shader_module_to_handle(&vs_module);
294	res = create_pipeline(device, vs_module_h);
295	if (res != VK_SUCCESS)
296		goto fail;
297
298	goto cleanup;
299
300fail:
301	radv_device_finish_meta_resolve_state(device);
302
303cleanup:
304	ralloc_free(vs_module.nir);
305
306	return res;
307}
308
309static void
310emit_resolve(struct radv_cmd_buffer *cmd_buffer,
311             const VkOffset2D *dest_offset,
312             const VkExtent2D *resolve_extent)
313{
314	struct radv_device *device = cmd_buffer->device;
315	VkCommandBuffer cmd_buffer_h = radv_cmd_buffer_to_handle(cmd_buffer);
316	uint32_t offset;
317	const struct vertex_attrs vertex_data[3] = {
318		{
319			.position = {
320				dest_offset->x,
321				dest_offset->y,
322			},
323		},
324		{
325			.position = {
326				dest_offset->x,
327				dest_offset->y + resolve_extent->height,
328			},
329		},
330		{
331			.position = {
332				dest_offset->x + resolve_extent->width,
333				dest_offset->y,
334			},
335		},
336	};
337
338	cmd_buffer->state.flush_bits |= RADV_CMD_FLAG_FLUSH_AND_INV_CB;
339	radv_cmd_buffer_upload_data(cmd_buffer, sizeof(vertex_data), 16, vertex_data, &offset);
340	struct radv_buffer vertex_buffer = {
341		.device = device,
342		.size = sizeof(vertex_data),
343		.bo = cmd_buffer->upload.upload_bo,
344		.offset = offset,
345	};
346
347	VkBuffer vertex_buffer_h = radv_buffer_to_handle(&vertex_buffer);
348
349	radv_CmdBindVertexBuffers(cmd_buffer_h,
350				  /*firstBinding*/ 0,
351				  /*bindingCount*/ 1,
352				  (VkBuffer[]) { vertex_buffer_h },
353				  (VkDeviceSize[]) { 0 });
354
355	VkPipeline pipeline_h = device->meta_state.resolve.pipeline;
356	RADV_FROM_HANDLE(radv_pipeline, pipeline, pipeline_h);
357
358	if (cmd_buffer->state.pipeline != pipeline) {
359		radv_CmdBindPipeline(cmd_buffer_h, VK_PIPELINE_BIND_POINT_GRAPHICS,
360				     pipeline_h);
361	}
362
363	radv_CmdDraw(cmd_buffer_h, 3, 1, 0, 0);
364	cmd_buffer->state.flush_bits |= RADV_CMD_FLAG_FLUSH_AND_INV_CB;
365	si_emit_cache_flush(cmd_buffer);
366}
367
368void radv_CmdResolveImage(
369	VkCommandBuffer                             cmd_buffer_h,
370	VkImage                                     src_image_h,
371	VkImageLayout                               src_image_layout,
372	VkImage                                     dest_image_h,
373	VkImageLayout                               dest_image_layout,
374	uint32_t                                    region_count,
375	const VkImageResolve*                       regions)
376{
377	RADV_FROM_HANDLE(radv_cmd_buffer, cmd_buffer, cmd_buffer_h);
378	RADV_FROM_HANDLE(radv_image, src_image, src_image_h);
379	RADV_FROM_HANDLE(radv_image, dest_image, dest_image_h);
380	struct radv_device *device = cmd_buffer->device;
381	struct radv_meta_saved_state saved_state;
382	VkDevice device_h = radv_device_to_handle(device);
383	bool use_compute_resolve = false;
384
385	/* we can use the hw resolve only for single full resolves */
386	if (region_count == 1) {
387		if (regions[0].srcOffset.x ||
388		    regions[0].srcOffset.y ||
389		    regions[0].srcOffset.z)
390			use_compute_resolve = true;
391		if (regions[0].dstOffset.x ||
392		    regions[0].dstOffset.y ||
393		    regions[0].dstOffset.z)
394			use_compute_resolve = true;
395
396		if (regions[0].extent.width != src_image->extent.width ||
397		    regions[0].extent.height != src_image->extent.height ||
398		    regions[0].extent.depth != src_image->extent.depth)
399			use_compute_resolve = true;
400	} else
401		use_compute_resolve = true;
402
403	if (use_compute_resolve) {
404
405		radv_fast_clear_flush_image_inplace(cmd_buffer, src_image);
406		radv_meta_resolve_compute_image(cmd_buffer,
407						src_image,
408						src_image_layout,
409						dest_image,
410						dest_image_layout,
411						region_count, regions);
412		return;
413	}
414
415	radv_meta_save_graphics_reset_vport_scissor(&saved_state, cmd_buffer);
416
417	assert(src_image->samples > 1);
418	assert(dest_image->samples == 1);
419
420	if (src_image->samples >= 16) {
421		/* See commit aa3f9aaf31e9056a255f9e0472ebdfdaa60abe54 for the
422		 * glBlitFramebuffer workaround for samples >= 16.
423		 */
424		radv_finishme("vkCmdResolveImage: need interpolation workaround when "
425			      "samples >= 16");
426	}
427
428	if (src_image->array_size > 1)
429		radv_finishme("vkCmdResolveImage: multisample array images");
430
431	for (uint32_t r = 0; r < region_count; ++r) {
432		const VkImageResolve *region = &regions[r];
433
434		/* From the Vulkan 1.0 spec:
435		 *
436		 *    - The aspectMask member of srcSubresource and dstSubresource must
437		 *      only contain VK_IMAGE_ASPECT_COLOR_BIT
438		 *
439		 *    - The layerCount member of srcSubresource and dstSubresource must
440		 *      match
441		 */
442		assert(region->srcSubresource.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT);
443		assert(region->dstSubresource.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT);
444		assert(region->srcSubresource.layerCount ==
445		       region->dstSubresource.layerCount);
446
447		const uint32_t src_base_layer =
448			radv_meta_get_iview_layer(src_image, &region->srcSubresource,
449						  &region->srcOffset);
450
451		const uint32_t dest_base_layer =
452			radv_meta_get_iview_layer(dest_image, &region->dstSubresource,
453						  &region->dstOffset);
454
455		/**
456		 * From Vulkan 1.0.6 spec: 18.6 Resolving Multisample Images
457		 *
458		 *    extent is the size in texels of the source image to resolve in width,
459		 *    height and depth. 1D images use only x and width. 2D images use x, y,
460		 *    width and height. 3D images use x, y, z, width, height and depth.
461		 *
462		 *    srcOffset and dstOffset select the initial x, y, and z offsets in
463		 *    texels of the sub-regions of the source and destination image data.
464		 *    extent is the size in texels of the source image to resolve in width,
465		 *    height and depth. 1D images use only x and width. 2D images use x, y,
466		 *    width and height. 3D images use x, y, z, width, height and depth.
467		 */
468		const struct VkExtent3D extent =
469			radv_sanitize_image_extent(src_image->type, region->extent);
470		const struct VkOffset3D dstOffset =
471			radv_sanitize_image_offset(dest_image->type, region->dstOffset);
472
473
474		for (uint32_t layer = 0; layer < region->srcSubresource.layerCount;
475		     ++layer) {
476
477			struct radv_image_view src_iview;
478			radv_image_view_init(&src_iview, cmd_buffer->device,
479					     &(VkImageViewCreateInfo) {
480						     .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
481							     .image = src_image_h,
482							     .viewType = radv_meta_get_view_type(src_image),
483							     .format = src_image->vk_format,
484							     .subresourceRange = {
485							     .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
486							     .baseMipLevel = region->srcSubresource.mipLevel,
487							     .levelCount = 1,
488							     .baseArrayLayer = src_base_layer + layer,
489							     .layerCount = 1,
490						     },
491							     },
492					     cmd_buffer, VK_IMAGE_USAGE_SAMPLED_BIT);
493
494			struct radv_image_view dest_iview;
495			radv_image_view_init(&dest_iview, cmd_buffer->device,
496					     &(VkImageViewCreateInfo) {
497						     .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
498							     .image = dest_image_h,
499							     .viewType = radv_meta_get_view_type(dest_image),
500							     .format = dest_image->vk_format,
501							     .subresourceRange = {
502							     .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
503							     .baseMipLevel = region->dstSubresource.mipLevel,
504							     .levelCount = 1,
505							     .baseArrayLayer = dest_base_layer + layer,
506							     .layerCount = 1,
507						     },
508							     },
509					     cmd_buffer, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
510
511			VkFramebuffer fb_h;
512			radv_CreateFramebuffer(device_h,
513					       &(VkFramebufferCreateInfo) {
514						       .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
515							       .attachmentCount = 2,
516							       .pAttachments = (VkImageView[]) {
517							       radv_image_view_to_handle(&src_iview),
518							       radv_image_view_to_handle(&dest_iview),
519						       },
520						       .width = radv_minify(dest_image->extent.width,
521									    region->dstSubresource.mipLevel),
522						       .height = radv_minify(dest_image->extent.height,
523									      region->dstSubresource.mipLevel),
524						       .layers = 1
525					       },
526					       &cmd_buffer->pool->alloc,
527					       &fb_h);
528
529			radv_CmdBeginRenderPass(cmd_buffer_h,
530						      &(VkRenderPassBeginInfo) {
531							      .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
532								      .renderPass = device->meta_state.resolve.pass,
533								      .framebuffer = fb_h,
534								      .renderArea = {
535								      .offset = {
536									      dstOffset.x,
537									      dstOffset.y,
538								      },
539								      .extent = {
540									      extent.width,
541									      extent.height,
542								      }
543							      },
544							      .clearValueCount = 0,
545							      .pClearValues = NULL,
546						      },
547						      VK_SUBPASS_CONTENTS_INLINE);
548
549			emit_resolve(cmd_buffer,
550				     &(VkOffset2D) {
551					     .x = dstOffset.x,
552					     .y = dstOffset.y,
553				     },
554				     &(VkExtent2D) {
555					     .width = extent.width,
556					     .height = extent.height,
557				     });
558
559			radv_CmdEndRenderPass(cmd_buffer_h);
560
561			radv_DestroyFramebuffer(device_h, fb_h,
562						&cmd_buffer->pool->alloc);
563		}
564	}
565
566	radv_meta_restore(&saved_state, cmd_buffer);
567}
568
569/**
570 * Emit any needed resolves for the current subpass.
571 */
572void
573radv_cmd_buffer_resolve_subpass(struct radv_cmd_buffer *cmd_buffer)
574{
575	struct radv_framebuffer *fb = cmd_buffer->state.framebuffer;
576	const struct radv_subpass *subpass = cmd_buffer->state.subpass;
577	struct radv_meta_saved_state saved_state;
578
579	/* FINISHME(perf): Skip clears for resolve attachments.
580	 *
581	 * From the Vulkan 1.0 spec:
582	 *
583	 *    If the first use of an attachment in a render pass is as a resolve
584	 *    attachment, then the loadOp is effectively ignored as the resolve is
585	 *    guaranteed to overwrite all pixels in the render area.
586	 */
587
588	if (!subpass->has_resolve)
589		return;
590
591	radv_meta_save_graphics_reset_vport_scissor(&saved_state, cmd_buffer);
592
593	for (uint32_t i = 0; i < subpass->color_count; ++i) {
594		VkAttachmentReference src_att = subpass->color_attachments[i];
595		VkAttachmentReference dest_att = subpass->resolve_attachments[i];
596		struct radv_image *dst_img = cmd_buffer->state.framebuffer->attachments[dest_att.attachment].attachment->image;
597		if (dest_att.attachment == VK_ATTACHMENT_UNUSED)
598			continue;
599
600		if (dst_img->surface.dcc_size) {
601			radv_initialize_dcc(cmd_buffer, dst_img, 0xffffffff);
602			cmd_buffer->state.attachments[dest_att.attachment].current_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
603		}
604
605		struct radv_subpass resolve_subpass = {
606			.color_count = 2,
607			.color_attachments = (VkAttachmentReference[]) { src_att, dest_att },
608			.depth_stencil_attachment = { .attachment = VK_ATTACHMENT_UNUSED },
609		};
610
611		radv_cmd_buffer_set_subpass(cmd_buffer, &resolve_subpass, false);
612
613		/* Subpass resolves must respect the render area. We can ignore the
614		 * render area here because vkCmdBeginRenderPass set the render area
615		 * with 3DSTATE_DRAWING_RECTANGLE.
616		 *
617		 * XXX(chadv): Does the hardware really respect
618		 * 3DSTATE_DRAWING_RECTANGLE when draing a 3DPRIM_RECTLIST?
619		 */
620		emit_resolve(cmd_buffer,
621			     &(VkOffset2D) { 0, 0 },
622			     &(VkExtent2D) { fb->width, fb->height });
623	}
624
625	cmd_buffer->state.subpass = subpass;
626	radv_meta_restore(&saved_state, cmd_buffer);
627}
628