diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 3ac335f95d..deae932b66 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -2015,10 +2015,6 @@ void VKGSRender::clear_surface(u32 mask) else fmt::throw_exception("Unreachable" HERE); } - - //Fush unconditionally - parameters might not persist - //TODO: Better parameter management for overlay passes - flush_command_queue(); } for (auto &rtt : m_rtts.m_bound_render_targets) diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.h b/rpcs3/Emu/RSX/VK/VKHelpers.h index a1093cea4d..a0bc2c2c8f 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.h +++ b/rpcs3/Emu/RSX/VK/VKHelpers.h @@ -586,12 +586,14 @@ namespace vk std::unordered_map m_format_properties; gpu_formats_support m_formats_support{}; gpu_shader_types_support m_shader_types_support{}; + bool m_stencil_export_support = false; std::unique_ptr m_allocator; VkDevice dev = VK_NULL_HANDLE; void get_physical_device_features(VkPhysicalDeviceFeatures& features) { supported_extensions instance_extensions(supported_extensions::instance); + supported_extensions device_extensions(supported_extensions::device, nullptr, pgpu); if (!instance_extensions.is_supported("VK_KHR_get_physical_device_properties2")) { @@ -599,8 +601,6 @@ namespace vk } else { - supported_extensions device_extensions(supported_extensions::device, nullptr, pgpu); - VkPhysicalDeviceFeatures2KHR features2; features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; features2.pNext = nullptr; @@ -621,6 +621,8 @@ namespace vk m_shader_types_support.allow_int8 = !!shader_support_info.shaderInt8; features = features2.features; } + + m_stencil_export_support = device_extensions.is_supported("VK_EXT_shader_stencil_export"); } public: @@ -774,6 +776,11 @@ namespace vk return m_shader_types_support; } + bool get_shader_stencil_export_support() const + { + return m_stencil_export_support; + } + mem_allocator_base* get_allocator() const { return m_allocator.get(); diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.h b/rpcs3/Emu/RSX/VK/VKOverlays.h index cf11294db1..f8efaba5bd 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.h +++ b/rpcs3/Emu/RSX/VK/VKOverlays.h @@ -105,10 +105,17 @@ namespace vk layout_info.setLayoutCount = 1; layout_info.pSetLayouts = &m_descriptor_layout; + std::vector push_constants = get_push_constants(); + if (!push_constants.empty()) + { + layout_info.pushConstantRangeCount = u32(push_constants.size()); + layout_info.pPushConstantRanges = push_constants.data(); + } + CHECK_RESULT(vkCreatePipelineLayout(*m_device, &layout_info, nullptr, &m_pipeline_layout)); } - virtual void update_uniforms(vk::glsl::program* /*program*/) + virtual void update_uniforms(vk::command_buffer& /*cmd*/, vk::glsl::program* /*program*/) { } @@ -131,6 +138,14 @@ namespace vk return fs_inputs; } + virtual void get_dynamic_state_entries(VkDynamicState* state_descriptors, VkPipelineDynamicStateCreateInfo& info) + {} + + virtual std::vector get_push_constants() + { + return {}; + } + void upload_vertex_data(f32 *data, u32 count) { check_heap(); @@ -171,6 +186,8 @@ namespace vk dynamic_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamic_state_descriptors[dynamic_state_info.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT; dynamic_state_descriptors[dynamic_state_info.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR; + + get_dynamic_state_entries(dynamic_state_descriptors, dynamic_state_info); dynamic_state_info.pDynamicStates = dynamic_state_descriptors; VkVertexInputBindingDescription vb = { 0, 16, VK_VERTEX_INPUT_RATE_VERTEX }; @@ -214,7 +231,7 @@ namespace vk return result; } - void load_program(const vk::command_buffer& cmd, VkRenderPass pass, const std::vector& src) + void load_program(vk::command_buffer& cmd, VkRenderPass pass, const std::vector& src) { vk::glsl::program *program = nullptr; auto found = m_program_cache.find(pass); @@ -241,7 +258,7 @@ namespace vk VK_FALSE, 0.f, 1.f, 0.f, 0.f, m_sampler_filter, m_sampler_filter, VK_SAMPLER_MIPMAP_MODE_NEAREST, VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK); } - update_uniforms(program); + update_uniforms(cmd, program); program->bind_uniform({ m_ubo.heap->value, m_ubo_offset, std::max(m_ubo_length, 4u) }, 0, m_descriptor_set); @@ -400,7 +417,7 @@ namespace vk renderpass_config.enable_depth_test(VK_COMPARE_OP_ALWAYS); } - void update_uniforms(vk::glsl::program* /*program*/) override + void update_uniforms(vk::command_buffer& /*cmd*/, vk::glsl::program* /*program*/) override { m_ubo_offset = (u32)m_ubo.alloc<256>(128); auto dst = (f32*)m_ubo.map(m_ubo_offset, 128); @@ -694,7 +711,7 @@ namespace vk false, true, desc->data, owner_uid); } - void update_uniforms(vk::glsl::program* /*program*/) override + void update_uniforms(vk::command_buffer& /*cmd*/, vk::glsl::program* /*program*/) override { m_ubo_offset = (u32)m_ubo.alloc<256>(128); auto dst = (f32*)m_ubo.map(m_ubo_offset, 128); @@ -808,7 +825,7 @@ namespace vk { "#version 450\n" "#extension GL_ARB_separate_shader_objects : enable\n" - "layout(std140, set=0, binding=0) uniform static_data{ vec4 regs[8]; };\n" + "layout(push_constant) uniform static_data{ vec4 regs[2]; };\n" "layout(location=0) out vec2 tc0;\n" "layout(location=1) out vec4 color;\n" "layout(location=2) out vec4 mask;\n" @@ -846,19 +863,29 @@ namespace vk renderpass_config.set_attachment_count(1); } - void update_uniforms(vk::glsl::program* /*program*/) override + std::vector get_push_constants() override { - m_ubo_offset = (u32)m_ubo.alloc<256>(128); - auto dst = (f32*)m_ubo.map(m_ubo_offset, 128); - dst[0] = clear_color.r; - dst[1] = clear_color.g; - dst[2] = clear_color.b; - dst[3] = clear_color.a; - dst[4] = colormask.r; - dst[5] = colormask.g; - dst[6] = colormask.b; - dst[7] = colormask.a; - m_ubo.unmap(); + VkPushConstantRange constant; + constant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + constant.offset = 0; + constant.size = 32; + + return { constant }; + } + + void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/) override + { + f32 data[8]; + data[0] = clear_color.r; + data[1] = clear_color.g; + data[2] = clear_color.b; + data[3] = clear_color.a; + data[4] = colormask.r; + data[5] = colormask.g; + data[6] = colormask.b; + data[7] = colormask.a; + + vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0, 32, data); } void set_up_viewport(vk::command_buffer &cmd, u16 max_w, u16 max_h) override diff --git a/rpcs3/Emu/RSX/VK/VKResolveHelper.cpp b/rpcs3/Emu/RSX/VK/VKResolveHelper.cpp index 94de236266..c7677a5226 100644 --- a/rpcs3/Emu/RSX/VK/VKResolveHelper.cpp +++ b/rpcs3/Emu/RSX/VK/VKResolveHelper.cpp @@ -37,8 +37,20 @@ namespace vk std::unordered_map> g_unresolve_helpers; std::unique_ptr g_depth_resolver; std::unique_ptr g_depth_unresolver; - std::unique_ptr g_depthstencil_resolverAMD; - std::unique_ptr g_depthstencil_unresolverAMD; + std::unique_ptr g_stencil_resolver; + std::unique_ptr g_stencil_unresolver; + std::unique_ptr g_depthstencil_resolver; + std::unique_ptr g_depthstencil_unresolver; + + template + void initialize_pass(std::unique_ptr& ptr, vk::render_device& dev, Args&&... extras) + { + if (!ptr) + { + ptr = std::make_unique(std::forward(extras)...); + ptr->create(dev); + } + } void resolve_image(vk::command_buffer& cmd, vk::viewable_image* dst, vk::viewable_image* src) { @@ -63,22 +75,23 @@ namespace vk if (src->aspect() & VK_IMAGE_ASPECT_STENCIL_BIT) { - if (!g_depthstencil_resolverAMD) + if (dev.get_shader_stencil_export_support()) { - g_depthstencil_resolverAMD.reset(new vk::depthstencil_resolve_AMD()); - g_depthstencil_resolverAMD->create(dev); + initialize_pass(g_depthstencil_resolver, dev); + g_depthstencil_resolver->run(cmd, src, dst, renderpass); } + else + { + initialize_pass(g_depth_resolver, dev); + initialize_pass(g_stencil_resolver, dev); - g_depthstencil_resolverAMD->run(cmd, src, dst, renderpass); + g_depth_resolver->run(cmd, src, dst, renderpass); + g_stencil_resolver->run(cmd, src, dst, renderpass); + } } else { - if (!g_depth_resolver) - { - g_depth_resolver.reset(new vk::depthonly_resolve()); - g_depth_resolver->create(dev); - } - + initialize_pass(g_depth_resolver, dev); g_depth_resolver->run(cmd, src, dst, renderpass); } } @@ -107,22 +120,23 @@ namespace vk if (src->aspect() & VK_IMAGE_ASPECT_STENCIL_BIT) { - if (!g_depthstencil_unresolverAMD) + if (dev.get_shader_stencil_export_support()) { - g_depthstencil_unresolverAMD.reset(new vk::depthstencil_unresolve_AMD()); - g_depthstencil_unresolverAMD->create(dev); + initialize_pass(g_depthstencil_unresolver, dev); + g_depthstencil_unresolver->run(cmd, dst, src, renderpass); } + else + { + initialize_pass(g_depth_unresolver, dev); + initialize_pass(g_stencil_unresolver, dev); - g_depthstencil_unresolverAMD->run(cmd, dst, src, renderpass); + g_depth_unresolver->run(cmd, dst, src, renderpass); + g_stencil_unresolver->run(cmd, dst, src, renderpass); + } } else { - if (!g_depth_unresolver) - { - g_depth_unresolver.reset(new vk::depthonly_unresolve()); - g_depth_unresolver->create(dev); - } - + initialize_pass(g_depth_unresolver, dev); g_depth_unresolver->run(cmd, dst, src, renderpass); } } @@ -149,10 +163,16 @@ namespace vk g_depth_resolver.reset(); } - if (g_depthstencil_resolverAMD) + if (g_stencil_resolver) { - g_depthstencil_resolverAMD->destroy(); - g_depthstencil_resolverAMD.reset(); + g_stencil_resolver->destroy(); + g_stencil_resolver.reset(); + } + + if (g_depthstencil_resolver) + { + g_depthstencil_resolver->destroy(); + g_depthstencil_resolver.reset(); } if (g_depth_unresolver) @@ -161,10 +181,16 @@ namespace vk g_depth_unresolver.reset(); } - if (g_depthstencil_unresolverAMD) + if (g_stencil_unresolver) { - g_depthstencil_unresolverAMD->destroy(); - g_depthstencil_unresolverAMD.reset(); + g_stencil_unresolver->destroy(); + g_stencil_unresolver.reset(); + } + + if (g_depthstencil_unresolver) + { + g_depthstencil_unresolver->destroy(); + g_depthstencil_unresolver.reset(); } } @@ -175,7 +201,9 @@ namespace vk if (g_depth_resolver) g_depth_resolver->free_resources(); if (g_depth_unresolver) g_depth_unresolver->free_resources(); - if (g_depthstencil_resolverAMD) g_depthstencil_resolverAMD->free_resources(); - if (g_depthstencil_unresolverAMD) g_depthstencil_unresolverAMD->free_resources(); + if (g_stencil_resolver) g_stencil_resolver->free_resources(); + if (g_stencil_unresolver) g_stencil_unresolver->free_resources(); + if (g_depthstencil_resolver) g_depthstencil_resolver->free_resources(); + if (g_depthstencil_unresolver) g_depthstencil_unresolver->free_resources(); } } \ No newline at end of file diff --git a/rpcs3/Emu/RSX/VK/VKResolveHelper.h b/rpcs3/Emu/RSX/VK/VKResolveHelper.h index 389c8d493f..acda3d0fab 100644 --- a/rpcs3/Emu/RSX/VK/VKResolveHelper.h +++ b/rpcs3/Emu/RSX/VK/VKResolveHelper.h @@ -165,6 +165,7 @@ namespace vk { u8 samples_x = 1; u8 samples_y = 1; + s32 static_parameters[4]; depth_resolve_base() { @@ -172,7 +173,7 @@ namespace vk renderpass_config.enable_depth_test(VK_COMPARE_OP_ALWAYS); } - void build(const std::string& kernel, const std::string& extensions, bool stencil_texturing, bool input_is_multisampled) + void build(const std::string& kernel, const std::string& extensions, const std::vector& inputs) { vs_src = "#version 450\n" @@ -187,17 +188,14 @@ namespace vk fs_src = "#version 420\n" "#extension GL_ARB_separate_shader_objects : enable\n"; - fs_src += extensions + + fs_src += extensions + "\n" - "layout(std140, set=0, binding=0) uniform static_data{ ivec4 regs[8]; };\n" - "layout(set=0, binding=1) uniform sampler2D fs0;\n"; + "layout(push_constant) uniform static_data{ ivec4 regs[1]; };\n"; - if (stencil_texturing) + int binding = 1; + for (const auto& input : inputs) { - m_num_usable_samplers = 2; - - fs_src += - "layout(set=0, binding=2) uniform usampler2D fs1;\n"; + fs_src += "layout(set=0, binding=" + std::to_string(binding++) + ") uniform " + input + ";\n"; } fs_src += @@ -208,28 +206,22 @@ namespace vk fs_src += kernel + "}\n"; - if (input_is_multisampled) - { - auto sampler_loc = fs_src.find("sampler2D fs0"); - fs_src.insert(sampler_loc + 9, "MS"); - - if (stencil_texturing) - { - sampler_loc = fs_src.find("sampler2D fs1"); - fs_src.insert(sampler_loc + 9, "MS"); - } - } - LOG_ERROR(RSX, "Resolve shader:\n%s", fs_src); } - void update_uniforms(vk::glsl::program* /*program*/) override + std::vector get_push_constants() override { - m_ubo_offset = (u32)m_ubo.alloc<256>(8); - auto dst = (s32*)m_ubo.map(m_ubo_offset, 128); - dst[0] = samples_x; - dst[1] = samples_y; - m_ubo.unmap(); + VkPushConstantRange constant; + constant.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + constant.offset = 0; + constant.size = 16; + + return { constant }; + } + + void update_uniforms(vk::command_buffer& cmd, vk::glsl::program* /*program*/) override + { + vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, 8, static_parameters); } void update_sample_configuration(vk::image* msaa_image) @@ -248,6 +240,9 @@ namespace vk default: fmt::throw_exception("Unsupported sample count %d" HERE, msaa_image->samples()); } + + static_parameters[0] = samples_x; + static_parameters[1] = samples_y; } }; @@ -258,13 +253,12 @@ namespace vk build( " ivec2 out_coord = ivec2(gl_FragCoord.xy);\n" " ivec2 in_coord = (out_coord / regs[0].xy);\n" - " ivec2 sample_loc = out_coord % ivec2(regs[0].xy);\n" + " ivec2 sample_loc = out_coord % regs[0].xy;\n" " int sample_index = sample_loc.x + (sample_loc.y * regs[0].y);\n" " float frag_depth = texelFetch(fs0, in_coord, sample_index).x;\n" " gl_FragDepth = frag_depth;\n", "", - false, - true); + { "sampler2DMS fs0" }); } void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass) @@ -292,8 +286,7 @@ namespace vk " float frag_depth = texelFetch(fs0, pixel_coord, 0).x;\n" " gl_FragDepth = frag_depth;\n", "", - false, - false); + { "sampler2D fs0" }); } void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass) @@ -312,9 +305,142 @@ namespace vk } }; - struct depthstencil_resolve_AMD : depth_resolve_base + struct stencilonly_resolve : depth_resolve_base { - depthstencil_resolve_AMD() + VkClearRect region{}; + VkClearAttachment clear_info{}; + + stencilonly_resolve() + { + renderpass_config.enable_stencil_test( + VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, // Always replace + VK_COMPARE_OP_ALWAYS, // Always pass + 0xFF, // Full write-through + 0xFF); // Write active bit + + renderpass_config.set_stencil_mask(0xFF); + renderpass_config.set_depth_mask(false); + + clear_info.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + region.baseArrayLayer = 0; + region.layerCount = 1; + + build( + " ivec2 out_coord = ivec2(gl_FragCoord.xy);\n" + " ivec2 in_coord = (out_coord / regs[0].xy);\n" + " ivec2 sample_loc = out_coord % regs[0].xy;\n" + " int sample_index = sample_loc.x + (sample_loc.y * regs[0].y);\n" + " uint frag_stencil = texelFetch(fs0, in_coord, sample_index).x;\n" + " if ((frag_stencil & uint(regs[0].z)) == 0) discard;\n", + "", + {"usampler2DMS fs0"}); + } + + void get_dynamic_state_entries(VkDynamicState* state_descriptors, VkPipelineDynamicStateCreateInfo& info) override + { + state_descriptors[info.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK; + } + + void emit_geometry(vk::command_buffer& cmd) override + { + vkCmdClearAttachments(cmd, 1, &clear_info, 1, ®ion); + + for (s32 write_mask = 0x1; write_mask <= 0x80; write_mask <<= 1) + { + vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, write_mask); + vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 8, 4, &write_mask); + + overlay_pass::emit_geometry(cmd); + } + } + + void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass) + { + update_sample_configuration(msaa_image); + auto stencil_view = msaa_image->get_view(0xDEADBEEF, rsx::default_remap_vector, VK_IMAGE_ASPECT_STENCIL_BIT); + + region.rect.extent.width = resolve_image->width(); + region.rect.extent.height = resolve_image->height(); + + overlay_pass::run( + cmd, + (u16)resolve_image->width(), (u16)resolve_image->height(), + resolve_image, stencil_view, + render_pass); + } + }; + + struct stencilonly_unresolve : depth_resolve_base + { + VkClearRect region{}; + VkClearAttachment clear_info{}; + + stencilonly_unresolve() + { + renderpass_config.enable_stencil_test( + VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, // Always replace + VK_COMPARE_OP_ALWAYS, // Always pass + 0xFF, // Full write-through + 0xFF); // Write active bit + + renderpass_config.set_stencil_mask(0xFF); + renderpass_config.set_depth_mask(false); + + clear_info.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT; + region.baseArrayLayer = 0; + region.layerCount = 1; + + build( + " ivec2 pixel_coord = ivec2(gl_FragCoord.xy);\n" + " pixel_coord *= regs[0].xy;\n" + " pixel_coord.x += (gl_SampleID % regs[0].x);\n" + " pixel_coord.y += (gl_SampleID / regs[0].x);\n" + " uint frag_stencil = texelFetch(fs0, pixel_coord, 0).x;\n" + " if ((frag_stencil & uint(regs[0].z)) == 0) discard;\n", + "", + { "usampler2D fs0" }); + } + + void get_dynamic_state_entries(VkDynamicState* state_descriptors, VkPipelineDynamicStateCreateInfo& info) override + { + state_descriptors[info.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK; + } + + void emit_geometry(vk::command_buffer& cmd) override + { + vkCmdClearAttachments(cmd, 1, &clear_info, 1, ®ion); + + for (s32 write_mask = 0x1; write_mask <= 0x80; write_mask <<= 1) + { + vkCmdSetStencilWriteMask(cmd, VK_STENCIL_FRONT_AND_BACK, write_mask); + vkCmdPushConstants(cmd, m_pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, 8, 4, &write_mask); + + overlay_pass::emit_geometry(cmd); + } + } + + void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass) + { + renderpass_config.set_multisample_state(msaa_image->samples(), 0xFFFF, true, false, false); + renderpass_config.set_multisample_shading_rate(1.f); + update_sample_configuration(msaa_image); + + auto stencil_view = resolve_image->get_view(0xAAE4, rsx::default_remap_vector, VK_IMAGE_ASPECT_STENCIL_BIT); + + region.rect.extent.width = resolve_image->width(); + region.rect.extent.height = resolve_image->height(); + + overlay_pass::run( + cmd, + (u16)msaa_image->width(), (u16)msaa_image->height(), + msaa_image, stencil_view, + render_pass); + } + }; + + struct depthstencil_resolve_EXT : depth_resolve_base + { + depthstencil_resolve_EXT() { renderpass_config.enable_stencil_test( VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, // Always replace @@ -322,6 +448,7 @@ namespace vk 0xFF, // Full write-through 0); // Unused + renderpass_config.set_stencil_mask(0xFF); m_num_usable_samplers = 2; build( @@ -336,8 +463,7 @@ namespace vk "#extension GL_ARB_shader_stencil_export : enable\n", - true, - true); + { "sampler2DMS fs0", "usampler2DMS fs1" }); } void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass) @@ -354,9 +480,9 @@ namespace vk } }; - struct depthstencil_unresolve_AMD : depth_resolve_base + struct depthstencil_unresolve_EXT : depth_resolve_base { - depthstencil_unresolve_AMD() + depthstencil_unresolve_EXT() { renderpass_config.enable_stencil_test( VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, VK_STENCIL_OP_REPLACE, // Always replace @@ -364,6 +490,7 @@ namespace vk 0xFF, // Full write-through 0); // Unused + renderpass_config.set_stencil_mask(0xFF); m_num_usable_samplers = 2; build( @@ -378,8 +505,7 @@ namespace vk "#extension GL_ARB_shader_stencil_export : enable\n", - true, - false); + { "sampler2D fs0", "usampler2D fs1" }); } void run(vk::command_buffer& cmd, vk::viewable_image* msaa_image, vk::viewable_image* resolve_image, VkRenderPass render_pass)