From 63d9cb37ececd3294e347779de6a94a64d998fbc Mon Sep 17 00:00:00 2001 From: kd-11 Date: Thu, 12 Apr 2018 14:13:13 +0300 Subject: [PATCH] rsx: Framebuffer fixes Primary: - Fix SET_SURFACE_CLEAR channel mask - it has been wrong for all these years! Layout is RGBA not ARGB/BGRA like other registers Other Fixes: - vk: Implement subchannel clears using overla pass - vk: Simplify and clean up state management - gl: Fix nullptr deref in case of failed subresource copy - vk/gl: Ignore float buffer clears as hardware seems to do --- rpcs3/Emu/RSX/GL/GLGSRender.cpp | 16 +- rpcs3/Emu/RSX/GL/GLGSRender.h | 10 +- rpcs3/Emu/RSX/VK/VKGSRender.cpp | 257 ++++++++++++++++------------- rpcs3/Emu/RSX/VK/VKGSRender.h | 1 + rpcs3/Emu/RSX/VK/VKHelpers.h | 158 ++++++++++++++++++ rpcs3/Emu/RSX/VK/VKOverlays.h | 165 ++++++++++++------ rpcs3/Emu/RSX/VK/VKProgramBuffer.h | 57 +++---- rpcs3/Emu/RSX/rsx_utils.h | 4 +- 8 files changed, 459 insertions(+), 209 deletions(-) diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 65f3e768c1..d832a91669 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -401,6 +401,8 @@ void GLGSRender::end() //Bind textures and resolve external copy operations std::chrono::time_point textures_start = steady_clock::now(); int unused_location; + void *unused = nullptr; + gl::texture_view* tmp_view; for (int i = 0; i < rsx::limits::fragment_textures_count; ++i) { @@ -417,10 +419,10 @@ void GLGSRender::end() { sampler_state->image_handle->bind(); } - else if (sampler_state->external_subresource_desc.external_handle) + else if (sampler_state->external_subresource_desc.external_handle && + (tmp_view = m_gl_texture_cache.create_temporary_subresource(unused, sampler_state->external_subresource_desc))) { - void *unused = nullptr; - m_gl_texture_cache.create_temporary_subresource(unused, sampler_state->external_subresource_desc)->bind(); + tmp_view->bind(); } else { @@ -967,10 +969,14 @@ void GLGSRender::clear_surface(u32 arg) case rsx::surface_color_format::x32: case rsx::surface_color_format::w16z16y16x16: case rsx::surface_color_format::w32z32y32x32: + { + //Nop + break; + } case rsx::surface_color_format::g8b8: { - //NOP - break; + colormask = rsx::get_g8b8_r8g8_colormask(colormask); + // Fall through } default: { diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index 42bd55ce33..7d00c75511 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -158,7 +158,7 @@ struct driver_state { if (!test_property(GL_COLOR_WRITEMASK, mask)) { - glColorMask(((mask & 0x20) ? 1 : 0), ((mask & 0x40) ? 1 : 0), ((mask & 0x80) ? 1 : 0), ((mask & 0x10) ? 1 : 0)); + glColorMask(((mask & 0x10) ? 1 : 0), ((mask & 0x20) ? 1 : 0), ((mask & 0x40) ? 1 : 0), ((mask & 0x80) ? 1 : 0)); properties[GL_COLOR_WRITEMASK] = mask; } } @@ -166,10 +166,10 @@ struct driver_state void color_mask(bool r, bool g, bool b, bool a) { u32 mask = 0; - if (r) mask |= 0x20; - if (g) mask |= 0x40; - if (b) mask |= 0x80; - if (a) mask |= 0x10; + if (r) mask |= 0x10; + if (g) mask |= 0x20; + if (b) mask |= 0x40; + if (a) mask |= 0x80; color_mask(mask); } diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index a51a6af494..4dad64522a 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -101,7 +101,7 @@ namespace vk case rsx::surface_color_format::g8b8: { - VkComponentMapping gb_rg = { VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R }; + VkComponentMapping gb_rg = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G }; return std::make_pair(VK_FORMAT_R8G8_UNORM, gb_rg); } @@ -625,6 +625,9 @@ VKGSRender::VKGSRender() : GSRender() m_depth_scaler.reset(new vk::depth_scaling_pass()); m_depth_scaler->create(*m_device); + m_attachment_clear_pass.reset(new vk::attachment_clear_pass()); + m_attachment_clear_pass->create(*m_device); + m_prog_buffer.reset(new VKProgramBuffer(m_render_passes.data())); if (g_cfg.video.disable_vertex_cache) @@ -632,7 +635,7 @@ VKGSRender::VKGSRender() : GSRender() else m_vertex_cache.reset(new vk::weak_vertex_cache()); - m_shaders_cache.reset(new vk::shader_cache(*m_prog_buffer.get(), "vulkan", "v1.2")); + m_shaders_cache.reset(new vk::shader_cache(*m_prog_buffer.get(), "vulkan", "v1.25")); open_command_buffer(); @@ -752,6 +755,10 @@ VKGSRender::~VKGSRender() m_depth_scaler->destroy(); m_depth_scaler.reset(); + //Attachment clear helper + m_attachment_clear_pass->destroy(); + m_attachment_clear_pass.reset(); + //Pipeline descriptors vkDestroyPipelineLayout(*m_device, pipeline_layout, nullptr); vkDestroyDescriptorSetLayout(*m_device, descriptor_layouts, nullptr); @@ -1076,7 +1083,6 @@ void VKGSRender::end() std::chrono::time_point textures_start = vertex_end; - auto ds = std::get<1>(m_rtts.m_bound_depth_stencil); //Check for data casts @@ -1084,6 +1090,13 @@ void VKGSRender::end() { if (ds->old_contents->info.format == VK_FORMAT_B8G8R8A8_UNORM) { + //This routine does not recover stencil data, initialize to 255 + VkClearDepthStencilValue clear_depth = { 1.f, 255 }; + VkImageSubresourceRange range = { VK_IMAGE_ASPECT_STENCIL_BIT, 0, 1, 0, 1 }; + change_image_layout(*m_current_command_buffer, ds, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + vkCmdClearDepthStencilImage(*m_current_command_buffer, ds->value, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clear_depth, 1, &range); + change_image_layout(*m_current_command_buffer, ds, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + auto rp = vk::get_render_pass_location(VK_FORMAT_UNDEFINED, ds->info.format, 0); auto render_pass = m_render_passes[rp]; m_depth_converter->run(*m_current_command_buffer, ds->width(), ds->height(), ds, ds->old_contents->get_view(0xAAE4, rsx::default_remap_vector), render_pass, m_framebuffers_to_clean); @@ -1678,41 +1691,96 @@ void VKGSRender::clear_surface(u32 mask) if (mask & 0x2) { - if (surface_depth_format == rsx::surface_depth_format::z24s8) + if (surface_depth_format == rsx::surface_depth_format::z24s8 && + rsx::method_registers.stencil_mask() != 0) { u8 clear_stencil = rsx::method_registers.stencil_clear_value(); - depth_stencil_clear_values.depthStencil.stencil = clear_stencil; depth_stencil_mask |= VK_IMAGE_ASPECT_STENCIL_BIT; } } - if (mask & 0xF0) + if (auto colormask = (mask & 0xF0)) { if (m_draw_buffers_count > 0) { - u8 clear_a = rsx::method_registers.clear_color_a(); - u8 clear_r = rsx::method_registers.clear_color_r(); - u8 clear_g = rsx::method_registers.clear_color_g(); - u8 clear_b = rsx::method_registers.clear_color_b(); - - color_clear_values.color.float32[0] = (float)clear_r / 255; - color_clear_values.color.float32[1] = (float)clear_g / 255; - color_clear_values.color.float32[2] = (float)clear_b / 255; - color_clear_values.color.float32[3] = (float)clear_a / 255; - - for (u32 index = 0; index < m_draw_buffers_count; ++index) + bool use_fast_clear = false; + bool ignore_clear = false; + switch (rsx::method_registers.surface_color()) { - clear_descriptors.push_back({ VK_IMAGE_ASPECT_COLOR_BIT, index, color_clear_values }); - } + case rsx::surface_color_format::x32: + case rsx::surface_color_format::w16z16y16x16: + case rsx::surface_color_format::w32z32y32x32: + //NOP + ignore_clear = true; + break; + case rsx::surface_color_format::g8b8: + colormask = rsx::get_g8b8_r8g8_colormask(colormask); + use_fast_clear = (colormask == (0x10 | 0x20)); + ignore_clear = (colormask == 0); + colormask |= (0x40 | 0x80); + break; + default: + use_fast_clear = (colormask == (0x10 | 0x20 | 0x40 | 0x80)); + break; + }; - for (auto &rtt : m_rtts.m_bound_render_targets) + if (!ignore_clear) { - if (std::get<0>(rtt) != 0) + u8 clear_a = rsx::method_registers.clear_color_a(); + u8 clear_r = rsx::method_registers.clear_color_r(); + u8 clear_g = rsx::method_registers.clear_color_g(); + u8 clear_b = rsx::method_registers.clear_color_b(); + + color_clear_values.color.float32[0] = (float)clear_r / 255; + color_clear_values.color.float32[1] = (float)clear_g / 255; + color_clear_values.color.float32[2] = (float)clear_b / 255; + color_clear_values.color.float32[3] = (float)clear_a / 255; + + if (use_fast_clear) { - std::get<1>(rtt)->dirty = false; - std::get<1>(rtt)->old_contents = nullptr; + for (u32 index = 0; index < m_draw_buffers_count; ++index) + { + clear_descriptors.push_back({ VK_IMAGE_ASPECT_COLOR_BIT, index, color_clear_values }); + } + } + else + { + color4f clear_color = + { + color_clear_values.color.float32[0], + color_clear_values.color.float32[1], + color_clear_values.color.float32[2], + color_clear_values.color.float32[3] + }; + + m_attachment_clear_pass->update_config(colormask, clear_color); + for (u32 index = 0; index < m_draw_buffers_count; ++index) + { + if (auto rtt = std::get<1>(m_rtts.m_bound_render_targets[index])) + { + vk::insert_texture_barrier(*m_current_command_buffer, rtt); + m_attachment_clear_pass->run(*m_current_command_buffer, rtt->width(), rtt->height(), + rtt, rtt->get_view(0xAAE4, rsx::default_remap_vector), + m_draw_fbo->info.renderPass, m_framebuffers_to_clean); + } + else + fmt::throw_exception("Unreachable" HERE); + } + + //Fush unconditinally - parameters might not persist + //TODO: Better parameter management for overlay passes + flush_command_queue(); + } + + for (auto &rtt : m_rtts.m_bound_render_targets) + { + if (std::get<0>(rtt) != 0) + { + std::get<1>(rtt)->dirty = false; + std::get<1>(rtt)->old_contents = nullptr; + } } } } @@ -1966,6 +2034,7 @@ void VKGSRender::process_swap_request(frame_context_t *ctx, bool free_resources) m_text_writer->reset_descriptors(); } + m_attachment_clear_pass->free_resources(); m_depth_converter->free_resources(); m_depth_scaler->free_resources(); m_ui_renderer->free_resources(); @@ -2154,22 +2223,32 @@ void VKGSRender::load_program(const vk::vertex_upload_info& vertex_info) vk::pipeline_props properties = {}; + // Input assembly bool emulated_primitive_type; - - properties.ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - properties.ia.topology = vk::get_appropriate_topology(rsx::method_registers.current_draw_clause.primitive, emulated_primitive_type); + properties.state.set_primitive_type(vk::get_appropriate_topology(rsx::method_registers.current_draw_clause.primitive, emulated_primitive_type)); const bool restarts_valid = rsx::method_registers.current_draw_clause.command == rsx::draw_command::indexed && !emulated_primitive_type && !rsx::method_registers.current_draw_clause.is_disjoint_primitive; if (rsx::method_registers.restart_index_enabled() && !vk::emulate_primitive_restart() && restarts_valid) - properties.ia.primitiveRestartEnable = VK_TRUE; - else - properties.ia.primitiveRestartEnable = VK_FALSE; + properties.state.enable_primitive_restart(); - for (int i = 0; i < 4; ++i) - { - properties.att_state[i].colorWriteMask = 0xf; - properties.att_state[i].blendEnable = VK_FALSE; - } + // Rasterizer state + properties.state.set_attachment_count(m_draw_buffers_count); + properties.state.set_depth_mask(rsx::method_registers.depth_write_enabled()); + properties.state.set_front_face(vk::get_front_face(rsx::method_registers.front_face_mode())); + properties.state.enable_depth_clamp(rsx::method_registers.depth_clamp_enabled() || !rsx::method_registers.depth_clip_enabled()); + properties.state.enable_depth_bias(true); + + if (rsx::method_registers.depth_test_enabled()) + properties.state.enable_depth_test(vk::get_compare_func(rsx::method_registers.depth_func())); + + if (rsx::method_registers.logic_op_enabled()) + properties.state.enable_logic_op(vk::get_logic_op(rsx::method_registers.logic_operation())); + + if (rsx::method_registers.depth_bounds_test_enabled()) + properties.state.enable_depth_bounds_test(); + + if (rsx::method_registers.cull_face_enabled()) + properties.state.enable_cull_face(vk::get_cull_face(rsx::method_registers.cull_face_mode())); bool color_mask_b = rsx::method_registers.color_mask_b(); bool color_mask_g = rsx::method_registers.color_mask_g(); @@ -2177,21 +2256,9 @@ void VKGSRender::load_program(const vk::vertex_upload_info& vertex_info) bool color_mask_a = rsx::method_registers.color_mask_a(); if (rsx::method_registers.surface_color() == rsx::surface_color_format::g8b8) - { - //Map GB components onto RG rsx::get_g8b8_r8g8_colormask(color_mask_r, color_mask_g, color_mask_b, color_mask_a); - } - VkColorComponentFlags mask = 0; - if (color_mask_a) mask |= VK_COLOR_COMPONENT_A_BIT; - if (color_mask_b) mask |= VK_COLOR_COMPONENT_B_BIT; - if (color_mask_g) mask |= VK_COLOR_COMPONENT_G_BIT; - if (color_mask_r) mask |= VK_COLOR_COMPONENT_R_BIT; - - for (u8 idx = 0; idx < m_draw_buffers_count; ++idx) - { - properties.att_state[idx].colorWriteMask = mask; - } + properties.state.set_color_mask(color_mask_r, color_mask_g, color_mask_b, color_mask_a); bool mrt_blend_enabled[] = { @@ -2238,84 +2305,50 @@ void VKGSRender::load_program(const vk::vertex_upload_info& vertex_info) { if (mrt_blend_enabled[idx]) { - properties.att_state[idx].blendEnable = VK_TRUE; - properties.att_state[idx].srcColorBlendFactor = sfactor_rgb; - properties.att_state[idx].dstColorBlendFactor = dfactor_rgb; - properties.att_state[idx].srcAlphaBlendFactor = sfactor_a; - properties.att_state[idx].dstAlphaBlendFactor = dfactor_a; - properties.att_state[idx].colorBlendOp = equation_rgb; - properties.att_state[idx].alphaBlendOp = equation_a; + properties.state.enable_blend(idx, sfactor_rgb, sfactor_a, dfactor_rgb, dfactor_a, equation_rgb, equation_a); } } } - properties.cs.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - properties.cs.attachmentCount = m_draw_buffers_count; - properties.cs.pAttachments = properties.att_state; - - if (rsx::method_registers.logic_op_enabled()) - { - properties.cs.logicOpEnable = true; - properties.cs.logicOp = vk::get_logic_op(rsx::method_registers.logic_operation()); - } - - properties.ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - properties.ds.depthWriteEnable = rsx::method_registers.depth_write_enabled() ? VK_TRUE : VK_FALSE; - - if (rsx::method_registers.depth_bounds_test_enabled()) - { - properties.ds.depthBoundsTestEnable = VK_TRUE; - } - else - properties.ds.depthBoundsTestEnable = VK_FALSE; - if (rsx::method_registers.stencil_test_enabled()) { - properties.ds.stencilTestEnable = VK_TRUE; - properties.ds.front.failOp = vk::get_stencil_op(rsx::method_registers.stencil_op_fail()); - properties.ds.front.passOp = vk::get_stencil_op(rsx::method_registers.stencil_op_zpass()); - properties.ds.front.depthFailOp = vk::get_stencil_op(rsx::method_registers.stencil_op_zfail()); - properties.ds.front.compareOp = vk::get_compare_func(rsx::method_registers.stencil_func()); - - if (rsx::method_registers.two_sided_stencil_test_enabled()) + if (!rsx::method_registers.two_sided_stencil_test_enabled()) { - properties.ds.back.failOp = vk::get_stencil_op(rsx::method_registers.back_stencil_op_fail()); - properties.ds.back.passOp = vk::get_stencil_op(rsx::method_registers.back_stencil_op_zpass()); - properties.ds.back.depthFailOp = vk::get_stencil_op(rsx::method_registers.back_stencil_op_zfail()); - properties.ds.back.compareOp = vk::get_compare_func(rsx::method_registers.back_stencil_func()); + properties.state.set_stencil_mask(rsx::method_registers.stencil_mask()); + + properties.state.enable_stencil_test( + vk::get_stencil_op(rsx::method_registers.stencil_op_fail()), + vk::get_stencil_op(rsx::method_registers.stencil_op_zfail()), + vk::get_stencil_op(rsx::method_registers.stencil_op_zpass()), + vk::get_compare_func(rsx::method_registers.stencil_func()), + rsx::method_registers.stencil_func_mask(), + rsx::method_registers.stencil_func_ref()); } else - properties.ds.back = properties.ds.front; + { + properties.state.set_stencil_mask_separate(0, rsx::method_registers.stencil_mask()); + properties.state.set_stencil_mask_separate(1, rsx::method_registers.back_stencil_mask()); + + properties.state.enable_stencil_test_separate(0, + vk::get_stencil_op(rsx::method_registers.stencil_op_fail()), + vk::get_stencil_op(rsx::method_registers.stencil_op_zfail()), + vk::get_stencil_op(rsx::method_registers.stencil_op_zpass()), + vk::get_compare_func(rsx::method_registers.stencil_func()), + rsx::method_registers.stencil_func_mask(), + rsx::method_registers.stencil_func_ref()); + + properties.state.enable_stencil_test_separate(1, + vk::get_stencil_op(rsx::method_registers.back_stencil_op_fail()), + vk::get_stencil_op(rsx::method_registers.back_stencil_op_zfail()), + vk::get_stencil_op(rsx::method_registers.back_stencil_op_zpass()), + vk::get_compare_func(rsx::method_registers.back_stencil_func()), + rsx::method_registers.back_stencil_func_mask(), + rsx::method_registers.back_stencil_func_ref()); + } } - else - properties.ds.stencilTestEnable = VK_FALSE; - - if (rsx::method_registers.depth_test_enabled()) - { - properties.ds.depthTestEnable = VK_TRUE; - properties.ds.depthCompareOp = vk::get_compare_func(rsx::method_registers.depth_func()); - } - else - properties.ds.depthTestEnable = VK_FALSE; - - properties.rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - properties.rs.polygonMode = VK_POLYGON_MODE_FILL; - properties.rs.depthClampEnable = rsx::method_registers.depth_clamp_enabled() || !rsx::method_registers.depth_clip_enabled(); - properties.rs.rasterizerDiscardEnable = VK_FALSE; - - //Disabled by setting factors to 0 as needed - properties.rs.depthBiasEnable = VK_TRUE; - - if (rsx::method_registers.cull_face_enabled()) - properties.rs.cullMode = vk::get_cull_face(rsx::method_registers.cull_face_mode()); - else - properties.rs.cullMode = VK_CULL_MODE_NONE; - - properties.rs.frontFace = vk::get_front_face(rsx::method_registers.front_face_mode()); properties.render_pass = m_render_passes[m_current_renderpass_id]; properties.render_pass_location = (int)m_current_renderpass_id; - properties.num_targets = m_draw_buffers_count; vk::enter_uninterruptible(); diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.h b/rpcs3/Emu/RSX/VK/VKGSRender.h index a335e1ad2e..bc215a651a 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.h +++ b/rpcs3/Emu/RSX/VK/VKGSRender.h @@ -264,6 +264,7 @@ private: std::unique_ptr m_depth_converter; std::unique_ptr m_depth_scaler; std::unique_ptr m_ui_renderer; + std::unique_ptr m_attachment_clear_pass; shared_mutex m_sampler_mutex; u64 surface_store_tag = 0; diff --git a/rpcs3/Emu/RSX/VK/VKHelpers.h b/rpcs3/Emu/RSX/VK/VKHelpers.h index cecac23e68..dd439c39a7 100644 --- a/rpcs3/Emu/RSX/VK/VKHelpers.h +++ b/rpcs3/Emu/RSX/VK/VKHelpers.h @@ -2098,6 +2098,164 @@ public: } }; + class graphics_pipeline_state + { + public: + VkPipelineInputAssemblyStateCreateInfo ia; + VkPipelineDepthStencilStateCreateInfo ds; + VkPipelineColorBlendAttachmentState att_state[4]; + VkPipelineColorBlendStateCreateInfo cs; + VkPipelineRasterizationStateCreateInfo rs; + + graphics_pipeline_state() + { + ia = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; + cs = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; + ds = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; + rs = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; + + for (int i = 0; i < 4; ++i) + { + att_state[i] = {}; + } + + rs.polygonMode = VK_POLYGON_MODE_FILL; + rs.cullMode = VK_CULL_MODE_NONE; + rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rs.lineWidth = 1.f; + } + + ~graphics_pipeline_state() + {} + + void set_primitive_type(VkPrimitiveTopology type) + { + ia.topology = type; + } + + void enable_primitive_restart(bool enable = true) + { + ia.primitiveRestartEnable = enable? VK_TRUE : VK_FALSE; + } + + void set_color_mask(bool r, bool g, bool b, bool a) + { + VkColorComponentFlags mask = 0; + if (a) mask |= VK_COLOR_COMPONENT_A_BIT; + if (b) mask |= VK_COLOR_COMPONENT_B_BIT; + if (g) mask |= VK_COLOR_COMPONENT_G_BIT; + if (r) mask |= VK_COLOR_COMPONENT_R_BIT; + + att_state[0].colorWriteMask = mask; + att_state[1].colorWriteMask = mask; + att_state[2].colorWriteMask = mask; + att_state[3].colorWriteMask = mask; + } + + void set_depth_mask(bool enable) + { + ds.depthWriteEnable = enable ? VK_TRUE : VK_FALSE; + } + + void set_stencil_mask(u32 mask) + { + ds.front.writeMask = mask; + ds.back.writeMask = mask; + } + + void set_stencil_mask_separate(int face, u32 mask) + { + if (!face) + ds.front.writeMask = mask; + else + ds.back.writeMask = mask; + } + + void enable_depth_test(VkCompareOp op) + { + ds.depthTestEnable = VK_TRUE; + ds.depthCompareOp = op; + } + + void enable_depth_clamp(bool enable = true) + { + rs.depthClampEnable = enable ? VK_TRUE : VK_FALSE; + } + + void enable_depth_bias(bool enable = true) + { + rs.depthBiasEnable = VK_TRUE; + } + + void enable_depth_bounds_test(bool enable = true) + { + ds.depthBoundsTestEnable = enable? VK_TRUE : VK_FALSE; + } + + void enable_blend(int mrt_index, VkBlendFactor src_factor_rgb, VkBlendFactor src_factor_a, + VkBlendFactor dst_factor_rgb, VkBlendFactor dst_factor_a, + VkBlendOp equation_rgb, VkBlendOp equation_a) + { + att_state[mrt_index].srcColorBlendFactor = src_factor_rgb; + att_state[mrt_index].srcAlphaBlendFactor = src_factor_a; + att_state[mrt_index].dstColorBlendFactor = dst_factor_rgb; + att_state[mrt_index].dstAlphaBlendFactor = dst_factor_a; + att_state[mrt_index].colorBlendOp = equation_rgb; + att_state[mrt_index].alphaBlendOp = equation_a; + att_state[mrt_index].blendEnable = VK_TRUE; + } + + void enable_stencil_test(VkStencilOp fail, VkStencilOp zfail, VkStencilOp pass, + VkCompareOp func, u32 func_mask, u32 ref) + { + ds.front.failOp = fail; + ds.front.passOp = pass; + ds.front.depthFailOp = zfail; + ds.front.compareOp = func; + ds.front.compareMask = func_mask; + ds.front.reference = ref; + ds.back = ds.front; + + ds.stencilTestEnable = VK_TRUE; + } + + void enable_stencil_test_separate(int face, VkStencilOp fail, VkStencilOp zfail, VkStencilOp pass, + VkCompareOp func, u32 func_mask, u32 ref) + { + auto& face_props = (face ? ds.back : ds.front); + face_props.failOp = fail; + face_props.passOp = pass; + face_props.depthFailOp = zfail; + face_props.compareOp = func; + face_props.compareMask = func_mask; + face_props.reference = ref; + + ds.stencilTestEnable = VK_TRUE; + } + + void enable_logic_op(VkLogicOp op) + { + cs.logicOpEnable = VK_TRUE; + cs.logicOp = op; + } + + void enable_cull_face(VkCullModeFlags cull_mode) + { + rs.cullMode = cull_mode; + } + + void set_front_face(VkFrontFace face) + { + rs.frontFace = face; + } + + void set_attachment_count(u32 count) + { + cs.attachmentCount = count; + cs.pAttachments = att_state; + } + }; + namespace glsl { enum program_input_type diff --git a/rpcs3/Emu/RSX/VK/VKOverlays.h b/rpcs3/Emu/RSX/VK/VKOverlays.h index 196bd4a433..59c942fb52 100644 --- a/rpcs3/Emu/RSX/VK/VKOverlays.h +++ b/rpcs3/Emu/RSX/VK/VKOverlays.h @@ -30,15 +30,7 @@ namespace vk std::string vs_src; std::string fs_src; - struct - { - int color_attachments = 0; - bool write_color = true; - bool write_depth = true; - bool no_depth_test = true; - bool enable_blend = false; - } - renderpass_config; + graphics_pipeline_state renderpass_config; bool initialized = false; bool compiled = false; @@ -46,6 +38,15 @@ namespace vk u32 num_drawable_elements = 4; u32 first_vertex = 0; + overlay_pass() + { + //Override-able defaults + renderpass_config.set_primitive_type(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP); + } + + ~overlay_pass() + {} + void init_descriptors() { VkDescriptorPoolSize descriptor_pool_sizes[2] = @@ -173,51 +174,16 @@ namespace vk ms.pSampleMask = NULL; ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; - VkPipelineInputAssemblyStateCreateInfo ia = {}; - ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; - ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; - - VkPipelineRasterizationStateCreateInfo rs = {}; - rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; - rs.lineWidth = 1.f; - rs.polygonMode = VK_POLYGON_MODE_FILL; - - VkPipelineColorBlendAttachmentState att = {}; - if (renderpass_config.write_color) - { - att.colorWriteMask = 0xf; - - if (renderpass_config.enable_blend) - { - att.blendEnable = VK_TRUE; - att.alphaBlendOp = VK_BLEND_OP_ADD; - att.colorBlendOp = VK_BLEND_OP_ADD; - att.dstAlphaBlendFactor = att.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - att.srcAlphaBlendFactor = att.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; - } - } - - VkPipelineColorBlendStateCreateInfo cs = {}; - cs.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; - cs.attachmentCount = renderpass_config.color_attachments; - cs.pAttachments = &att; - - VkPipelineDepthStencilStateCreateInfo ds = {}; - ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; - ds.depthWriteEnable = renderpass_config.write_depth? VK_TRUE: VK_FALSE; - ds.depthTestEnable = VK_TRUE; - ds.depthCompareOp = VK_COMPARE_OP_ALWAYS; - VkPipeline pipeline; VkGraphicsPipelineCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; info.pVertexInputState = &vi; - info.pInputAssemblyState = &ia; - info.pRasterizationState = &rs; - info.pColorBlendState = &cs; + info.pInputAssemblyState = &renderpass_config.ia; + info.pRasterizationState = &renderpass_config.rs; + info.pColorBlendState = &renderpass_config.cs; info.pMultisampleState = &ms; info.pViewportState = &vp; - info.pDepthStencilState = &ds; + info.pDepthStencilState = &renderpass_config.ds; info.stageCount = 2; info.pStages = shader_stages; info.pDynamicState = &dynamic_state_info; @@ -426,7 +392,8 @@ namespace vk "}\n" }; - renderpass_config.write_color = false; + renderpass_config.set_depth_mask(true); + renderpass_config.enable_depth_test(VK_COMPARE_OP_ALWAYS); m_vertex_shader.id = 100002; m_fragment_shader.id = 100003; @@ -507,10 +474,13 @@ namespace vk "}\n" }; - renderpass_config.color_attachments = 1; - renderpass_config.write_color = true; - renderpass_config.write_depth = false; - renderpass_config.enable_blend = true; + renderpass_config.set_attachment_count(1); + renderpass_config.set_color_mask(true, true, true, true); + renderpass_config.set_depth_mask(false); + renderpass_config.enable_blend(0, + VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_SRC_ALPHA, + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + VK_BLEND_OP_ADD, VK_BLEND_OP_ADD); m_vertex_shader.id = 100004; m_fragment_shader.id = 100005; @@ -749,9 +719,96 @@ namespace vk "}\n" }; - renderpass_config.write_color = false; + renderpass_config.set_depth_mask(true); + renderpass_config.enable_depth_test(VK_COMPARE_OP_ALWAYS); + m_vertex_shader.id = 100006; m_fragment_shader.id = 100007; } }; + + struct attachment_clear_pass : public overlay_pass + { + color4f clear_color = { 0.f, 0.f, 0.f, 0.f }; + color4f colormask = { 1.f, 1.f, 1.f, 1.f }; + + attachment_clear_pass() + { + vs_src = + { + "#version 450\n" + "#extension GL_ARB_separate_shader_objects : enable\n" + "layout(std140, set=0, binding=1) uniform static_data{ vec4 regs[8]; };\n" + "layout(location=0) out vec2 tc0;\n" + "layout(location=1) out vec4 color;\n" + "layout(location=2) out vec4 mask;\n" + "\n" + "void main()\n" + "{\n" + " vec2 positions[] = {vec2(-1., -1.), vec2(1., -1.), vec2(-1., 1.), vec2(1., 1.)};\n" + " vec2 coords[] = {vec2(0., 0.), vec2(1., 0.), vec2(0., 1.), vec2(1., 1.)};\n" + " tc0 = coords[gl_VertexIndex % 4];\n" + " color = regs[0];\n" + " mask = regs[1];\n" + " gl_Position = vec4(positions[gl_VertexIndex % 4], 0., 1.);\n" + "}\n" + }; + + fs_src = + { + "#version 420\n" + "#extension GL_ARB_separate_shader_objects : enable\n" + "layout(set=0, binding=0) uniform sampler2D fs0;\n" + "layout(location=0) in vec2 tc0;\n" + "layout(location=1) in vec4 color;\n" + "layout(location=2) in vec4 mask;\n" + "layout(location=0) out vec4 out_color;\n" + "\n" + "void main()\n" + "{\n" + " vec4 original_color = texture(fs0, tc0);\n" + " out_color = mix(original_color, color, bvec4(mask));\n" + "}\n" + }; + + renderpass_config.set_depth_mask(false); + renderpass_config.set_color_mask(true, true, true, true); + renderpass_config.set_attachment_count(1); + + m_vertex_shader.id = 100006; + m_fragment_shader.id = 100007; + } + + void update_uniforms(vk::glsl::program* /*program*/) override + { + auto dst = (f32*)m_ubo->map(0, 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(); + } + + bool update_config(u32 clearmask, color4f color) + { + color4f mask = { 0.f, 0.f, 0.f, 0.f }; + if (clearmask & 0x10) mask.r = 1.f; + if (clearmask & 0x20) mask.g = 1.f; + if (clearmask & 0x40) mask.b = 1.f; + if (clearmask & 0x80) mask.a = 1.f; + + if (mask != colormask || color != clear_color) + { + colormask = mask; + clear_color = color; + return true; + } + + return false; + } + }; } diff --git a/rpcs3/Emu/RSX/VK/VKProgramBuffer.h b/rpcs3/Emu/RSX/VK/VKProgramBuffer.h index 8049084e4b..7ee0cab0f5 100644 --- a/rpcs3/Emu/RSX/VK/VKProgramBuffer.h +++ b/rpcs3/Emu/RSX/VK/VKProgramBuffer.h @@ -3,45 +3,41 @@ #include "VKFragmentProgram.h" #include "../Common/ProgramStateCache.h" #include "Utilities/hash.h" +#include "VKHelpers.h" namespace vk { struct pipeline_props { - VkPipelineInputAssemblyStateCreateInfo ia; - VkPipelineDepthStencilStateCreateInfo ds; - VkPipelineColorBlendAttachmentState att_state[4]; - VkPipelineColorBlendStateCreateInfo cs; - VkPipelineRasterizationStateCreateInfo rs; - + graphics_pipeline_state state; VkRenderPass render_pass; int num_targets; int render_pass_location; bool operator==(const pipeline_props& other) const { - if (memcmp(&att_state[0], &other.att_state[0], sizeof(VkPipelineColorBlendAttachmentState))) + if (memcmp(&state.att_state[0], &other.state.att_state[0], sizeof(VkPipelineColorBlendAttachmentState))) return false; if (render_pass_location != other.render_pass_location) return false; - if (memcmp(&rs, &other.rs, sizeof(VkPipelineRasterizationStateCreateInfo))) + if (memcmp(&state.rs, &other.state.rs, sizeof(VkPipelineRasterizationStateCreateInfo))) return false; //Cannot memcmp cs due to pAttachments being a pointer to memory - if (cs.attachmentCount != other.cs.attachmentCount || - cs.flags != other.cs.flags || - cs.logicOp != other.cs.logicOp || - cs.logicOpEnable != other.cs.logicOpEnable || - cs.sType != other.cs.sType || - memcmp(cs.blendConstants, other.cs.blendConstants, 4 * sizeof(f32))) + if (state.cs.attachmentCount != other.state.cs.attachmentCount || + state.cs.flags != other.state.cs.flags || + state.cs.logicOp != other.state.cs.logicOp || + state.cs.logicOpEnable != other.state.cs.logicOpEnable || + state.cs.sType != other.state.cs.sType || + memcmp(state.cs.blendConstants, other.state.cs.blendConstants, 4 * sizeof(f32))) return false; - if (memcmp(&ia, &other.ia, sizeof(VkPipelineInputAssemblyStateCreateInfo))) + if (memcmp(&state.ia, &other.state.ia, sizeof(VkPipelineInputAssemblyStateCreateInfo))) return false; - if (memcmp(&ds, &other.ds, sizeof(VkPipelineDepthStencilStateCreateInfo))) + if (memcmp(&state.ds, &other.state.ds, sizeof(VkPipelineDepthStencilStateCreateInfo))) return false; if (num_targets != other.num_targets) @@ -58,16 +54,16 @@ namespace rpcs3 size_t hash_struct(const vk::pipeline_props &pipelineProperties) { size_t seed = hash_base(pipelineProperties.num_targets); - seed ^= hash_struct(pipelineProperties.ia); - seed ^= hash_struct(pipelineProperties.ds); - seed ^= hash_struct(pipelineProperties.rs); + seed ^= hash_struct(pipelineProperties.state.ia); + seed ^= hash_struct(pipelineProperties.state.ds); + seed ^= hash_struct(pipelineProperties.state.rs); //Do not compare pointers to memory! - auto tmp = pipelineProperties.cs; + auto tmp = pipelineProperties.state.cs; tmp.pAttachments = nullptr; seed ^= hash_struct(tmp); - seed ^= hash_struct(pipelineProperties.att_state[0]); + seed ^= hash_struct(pipelineProperties.state.att_state[0]); return hash_base(seed); } } @@ -99,17 +95,16 @@ struct VKTraits void validate_pipeline_properties(const VKVertexProgram&, const VKFragmentProgram &fp, vk::pipeline_props& properties) { //Explicitly disable writing to undefined registers - properties.att_state[0].colorWriteMask &= fp.output_color_masks[0]; - properties.att_state[1].colorWriteMask &= fp.output_color_masks[1]; - properties.att_state[2].colorWriteMask &= fp.output_color_masks[2]; - properties.att_state[3].colorWriteMask &= fp.output_color_masks[3]; + properties.state.att_state[0].colorWriteMask &= fp.output_color_masks[0]; + properties.state.att_state[1].colorWriteMask &= fp.output_color_masks[1]; + properties.state.att_state[2].colorWriteMask &= fp.output_color_masks[2]; + properties.state.att_state[3].colorWriteMask &= fp.output_color_masks[3]; } static pipeline_storage_type build_pipeline(const vertex_program_type &vertexProgramData, const fragment_program_type &fragmentProgramData, const vk::pipeline_props &pipelineProperties, VkDevice dev, VkPipelineLayout common_pipeline_layout) { - VkPipelineShaderStageCreateInfo shader_stages[2] = {}; shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; @@ -151,12 +146,12 @@ struct VKTraits VkGraphicsPipelineCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; info.pVertexInputState = &vi; - info.pInputAssemblyState = &pipelineProperties.ia; - info.pRasterizationState = &pipelineProperties.rs; - info.pColorBlendState = &pipelineProperties.cs; + info.pInputAssemblyState = &pipelineProperties.state.ia; + info.pRasterizationState = &pipelineProperties.state.rs; + info.pColorBlendState = &pipelineProperties.state.cs; info.pMultisampleState = &ms; info.pViewportState = &vp; - info.pDepthStencilState = &pipelineProperties.ds; + info.pDepthStencilState = &pipelineProperties.state.ds; info.stageCount = 2; info.pStages = shader_stages; info.pDynamicState = &dynamic_state_info; @@ -208,7 +203,7 @@ public: { //Extract pointers from pipeline props props.render_pass = m_render_pass_data[props.render_pass_location]; - props.cs.pAttachments = props.att_state; + props.state.cs.pAttachments = props.state.att_state; vp.skip_vertex_input_check = true; getGraphicPipelineState(vp, fp, props, std::forward(args)...); } diff --git a/rpcs3/Emu/RSX/rsx_utils.h b/rpcs3/Emu/RSX/rsx_utils.h index c9bd240056..183c0e6682 100644 --- a/rpcs3/Emu/RSX/rsx_utils.h +++ b/rpcs3/Emu/RSX/rsx_utils.h @@ -456,8 +456,8 @@ namespace rsx static inline u32 get_g8b8_r8g8_colormask(u32 mask) { u32 result = 0; - if (mask & 0x40) result |= 0x40; - if (mask & 0x80) result |= 0x20; + if (mask & 0x20) result |= 0x20; + if (mask & 0x40) result |= 0x10; return result; }