diff --git a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp index 7beab46474..a9d69f3a10 100644 --- a/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp +++ b/rpcs3/Emu/RSX/Core/RSXDrawCommands.cpp @@ -2,6 +2,8 @@ #include "RSXDrawCommands.h" #include "Emu/RSX/Common/BufferUtils.h" +#include "Emu/RSX/Common/buffer_stream.hpp" +#include "Emu/RSX/Program/GLSLCommon.h" #include "Emu/RSX/rsx_methods.h" #include "Emu/RSX/RSXThread.h" @@ -555,4 +557,178 @@ namespace rsx } } } + + void draw_command_processor::fill_scale_offset_data(void* buffer, bool flip_y) const + { + int clip_w = rsx::method_registers.surface_clip_width(); + int clip_h = rsx::method_registers.surface_clip_height(); + + float scale_x = rsx::method_registers.viewport_scale_x() / (clip_w / 2.f); + float offset_x = rsx::method_registers.viewport_offset_x() - (clip_w / 2.f); + offset_x /= clip_w / 2.f; + + float scale_y = rsx::method_registers.viewport_scale_y() / (clip_h / 2.f); + float offset_y = (rsx::method_registers.viewport_offset_y() - (clip_h / 2.f)); + offset_y /= clip_h / 2.f; + if (flip_y) scale_y *= -1; + if (flip_y) offset_y *= -1; + + float scale_z = rsx::method_registers.viewport_scale_z(); + float offset_z = rsx::method_registers.viewport_offset_z(); + float one = 1.f; + + utils::stream_vector(buffer, std::bit_cast(scale_x), 0, 0, std::bit_cast(offset_x)); + utils::stream_vector(static_cast(buffer) + 16, 0, std::bit_cast(scale_y), 0, std::bit_cast(offset_y)); + utils::stream_vector(static_cast(buffer) + 32, 0, 0, std::bit_cast(scale_z), std::bit_cast(offset_z)); + utils::stream_vector(static_cast(buffer) + 48, 0, 0, 0, std::bit_cast(one)); + } + + void draw_command_processor::fill_user_clip_data(void* buffer) const + { + const rsx::user_clip_plane_op clip_plane_control[6] = + { + rsx::method_registers.clip_plane_0_enabled(), + rsx::method_registers.clip_plane_1_enabled(), + rsx::method_registers.clip_plane_2_enabled(), + rsx::method_registers.clip_plane_3_enabled(), + rsx::method_registers.clip_plane_4_enabled(), + rsx::method_registers.clip_plane_5_enabled(), + }; + + u8 data_block[64]; + s32* clip_enabled_flags = reinterpret_cast(data_block); + f32* clip_distance_factors = reinterpret_cast(data_block + 32); + + for (int index = 0; index < 6; ++index) + { + switch (clip_plane_control[index]) + { + default: + rsx_log.error("bad clip plane control (0x%x)", static_cast(clip_plane_control[index])); + [[fallthrough]]; + + case rsx::user_clip_plane_op::disable: + clip_enabled_flags[index] = 0; + clip_distance_factors[index] = 0.f; + break; + + case rsx::user_clip_plane_op::greater_or_equal: + clip_enabled_flags[index] = 1; + clip_distance_factors[index] = 1.f; + break; + + case rsx::user_clip_plane_op::less_than: + clip_enabled_flags[index] = 1; + clip_distance_factors[index] = -1.f; + break; + } + } + + memcpy(buffer, data_block, 2 * 8 * sizeof(u32)); + } + + /** + * Fill buffer with vertex program constants. + * Buffer must be at least 512 float4 wide. + */ + void draw_command_processor::fill_vertex_program_constants_data(void* buffer, const std::span& reloc_table) + { + if (!reloc_table.empty()) [[ likely ]] + { + char* dst = reinterpret_cast(buffer); + for (const auto& index : reloc_table) + { + utils::stream_vector_from_memory(dst, &rsx::method_registers.transform_constants[index]); + dst += 16; + } + } + else + { + memcpy(buffer, rsx::method_registers.transform_constants.data(), 468 * 4 * sizeof(float)); + } + } + + void draw_command_processor::fill_fragment_state_buffer(void* buffer, const RSXFragmentProgram& /*fragment_program*/) + { + ROP_control_t rop_control{}; + + if (rsx::method_registers.alpha_test_enabled()) + { + const u32 alpha_func = static_cast(rsx::method_registers.alpha_func()); + rop_control.set_alpha_test_func(alpha_func); + rop_control.enable_alpha_test(); + } + + if (rsx::method_registers.polygon_stipple_enabled()) + { + rop_control.enable_polygon_stipple(); + } + + if (rsx::method_registers.msaa_alpha_to_coverage_enabled() && !m_thread->get_backend_config().supports_hw_a2c) + { + // TODO: Properly support alpha-to-coverage and alpha-to-one behavior in shaders + // Alpha values generate a coverage mask for order independent blending + // Requires hardware AA to work properly (or just fragment sample stage in fragment shaders) + // Simulated using combined alpha blend and alpha test + rop_control.enable_alpha_to_coverage(); + if (rsx::method_registers.msaa_sample_mask()) + { + rop_control.enable_MSAA_writes(); + } + + // Sample configuration bits + switch (rsx::method_registers.surface_antialias()) + { + case rsx::surface_antialiasing::center_1_sample: + break; + case rsx::surface_antialiasing::diagonal_centered_2_samples: + rop_control.set_msaa_control(1u); + break; + default: + rop_control.set_msaa_control(3u); + break; + } + } + + const f32 fog0 = rsx::method_registers.fog_params_0(); + const f32 fog1 = rsx::method_registers.fog_params_1(); + const u32 fog_mode = static_cast(rsx::method_registers.fog_equation()); + + // Check if framebuffer is actually an XRGB format and not a WZYX format + switch (rsx::method_registers.surface_color()) + { + case rsx::surface_color_format::w16z16y16x16: + case rsx::surface_color_format::w32z32y32x32: + case rsx::surface_color_format::x32: + // These behave very differently from "normal" formats. + break; + default: + // Integer framebuffer formats. + rop_control.enable_framebuffer_INT(); + + // Check if we want sRGB conversion. + if (rsx::method_registers.framebuffer_srgb_enabled()) + { + rop_control.enable_framebuffer_sRGB(); + } + break; + } + + // Generate wpos coefficients + // wpos equation is now as follows: + // wpos.y = (frag_coord / resolution_scale) * ((window_origin!=top)?-1.: 1.) + ((window_origin!=top)? window_height : 0) + // wpos.x = (frag_coord / resolution_scale) + // wpos.zw = frag_coord.zw + + const auto window_origin = rsx::method_registers.shader_window_origin(); + const u32 window_height = rsx::method_registers.shader_window_height(); + const f32 resolution_scale = (window_height <= static_cast(g_cfg.video.min_scalable_dimension)) ? 1.f : rsx::get_resolution_scale(); + const f32 wpos_scale = (window_origin == rsx::window_origin::top) ? (1.f / resolution_scale) : (-1.f / resolution_scale); + const f32 wpos_bias = (window_origin == rsx::window_origin::top) ? 0.f : window_height; + const f32 alpha_ref = rsx::method_registers.alpha_ref(); + + u32* dst = static_cast(buffer); + utils::stream_vector(dst, std::bit_cast(fog0), std::bit_cast(fog1), rop_control.value, std::bit_cast(alpha_ref)); + utils::stream_vector(dst + 4, 0u, fog_mode, std::bit_cast(wpos_scale), std::bit_cast(wpos_bias)); + } } diff --git a/rpcs3/Emu/RSX/Core/RSXDrawCommands.h b/rpcs3/Emu/RSX/Core/RSXDrawCommands.h index 2bdf8b05b8..06570eac5c 100644 --- a/rpcs3/Emu/RSX/Core/RSXDrawCommands.h +++ b/rpcs3/Emu/RSX/Core/RSXDrawCommands.h @@ -77,5 +77,29 @@ namespace rsx u32 vertex_count, void* persistent_data, void* volatile_data) const; + + /** + * Fill buffer with 4x4 scale offset matrix. + * Vertex shader's position is to be multiplied by this matrix. + * if flip_y is set, the matrix is modified to use d3d convention. + */ + void fill_scale_offset_data(void* buffer, bool flip_y) const; + + /** + * Fill buffer with user clip information + */ + void fill_user_clip_data(void* buffer) const; + + /** + * Fill buffer with vertex program constants. + * Relocation table allows to do a partial fill with only selected registers. + */ + void fill_vertex_program_constants_data(void* buffer, const std::span& reloc_table); + + /** + * Fill buffer with fragment rasterization state. + * Fills current fog values, alpha test parameters and texture scaling parameters + */ + void fill_fragment_state_buffer(void* buffer, const RSXFragmentProgram& fragment_program); }; } diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 846567e595..3e60af9f68 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -840,8 +840,8 @@ void GLGSRender::load_program_env() // Vertex state auto mapping = m_vertex_env_buffer->alloc_from_heap(144, m_uniform_buffer_offset_align); auto buf = static_cast(mapping.first); - fill_scale_offset_data(buf, false); - fill_user_clip_data(buf + 64); + m_draw_processor.fill_scale_offset_data(buf, false); + m_draw_processor.fill_user_clip_data(buf + 64); *(reinterpret_cast(buf + 128)) = rsx::method_registers.transform_branch_bits(); *(reinterpret_cast(buf + 132)) = rsx::method_registers.point_size() * rsx::get_resolution_scale(); *(reinterpret_cast(buf + 136)) = rsx::method_registers.clip_min(); @@ -887,7 +887,7 @@ void GLGSRender::load_program_env() // Fragment state auto mapping = m_fragment_env_buffer->alloc_from_heap(32, m_uniform_buffer_offset_align); auto buf = static_cast(mapping.first); - fill_fragment_state_buffer(buf, current_fragment_program); + m_draw_processor.fill_fragment_state_buffer(buf, current_fragment_program); m_fragment_env_buffer->bind_range(GL_FRAGMENT_STATE_BIND_SLOT, mapping.second, 32); } @@ -988,7 +988,7 @@ void GLGSRender::upload_transform_constants(const rsx::io_buffer& buffer) : std::span(m_vertex_prog->constant_ids); buffer.reserve(transform_constants_size); - fill_vertex_program_constants_data(buffer.data(), constant_ids); + m_draw_processor.fill_vertex_program_constants_data(buffer.data(), constant_ids); } } diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index ea67bd6933..a7ed9894e1 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -1153,180 +1153,6 @@ namespace rsx state += cpu_flag::exit; } - void thread::fill_scale_offset_data(void *buffer, bool flip_y) const - { - int clip_w = rsx::method_registers.surface_clip_width(); - int clip_h = rsx::method_registers.surface_clip_height(); - - float scale_x = rsx::method_registers.viewport_scale_x() / (clip_w / 2.f); - float offset_x = rsx::method_registers.viewport_offset_x() - (clip_w / 2.f); - offset_x /= clip_w / 2.f; - - float scale_y = rsx::method_registers.viewport_scale_y() / (clip_h / 2.f); - float offset_y = (rsx::method_registers.viewport_offset_y() - (clip_h / 2.f)); - offset_y /= clip_h / 2.f; - if (flip_y) scale_y *= -1; - if (flip_y) offset_y *= -1; - - float scale_z = rsx::method_registers.viewport_scale_z(); - float offset_z = rsx::method_registers.viewport_offset_z(); - float one = 1.f; - - utils::stream_vector(buffer, std::bit_cast(scale_x), 0, 0, std::bit_cast(offset_x)); - utils::stream_vector(static_cast(buffer) + 16, 0, std::bit_cast(scale_y), 0, std::bit_cast(offset_y)); - utils::stream_vector(static_cast(buffer) + 32, 0, 0, std::bit_cast(scale_z), std::bit_cast(offset_z)); - utils::stream_vector(static_cast(buffer) + 48, 0, 0, 0, std::bit_cast(one)); - } - - void thread::fill_user_clip_data(void *buffer) const - { - const rsx::user_clip_plane_op clip_plane_control[6] = - { - rsx::method_registers.clip_plane_0_enabled(), - rsx::method_registers.clip_plane_1_enabled(), - rsx::method_registers.clip_plane_2_enabled(), - rsx::method_registers.clip_plane_3_enabled(), - rsx::method_registers.clip_plane_4_enabled(), - rsx::method_registers.clip_plane_5_enabled(), - }; - - u8 data_block[64]; - s32* clip_enabled_flags = reinterpret_cast(data_block); - f32* clip_distance_factors = reinterpret_cast(data_block + 32); - - for (int index = 0; index < 6; ++index) - { - switch (clip_plane_control[index]) - { - default: - rsx_log.error("bad clip plane control (0x%x)", static_cast(clip_plane_control[index])); - [[fallthrough]]; - - case rsx::user_clip_plane_op::disable: - clip_enabled_flags[index] = 0; - clip_distance_factors[index] = 0.f; - break; - - case rsx::user_clip_plane_op::greater_or_equal: - clip_enabled_flags[index] = 1; - clip_distance_factors[index] = 1.f; - break; - - case rsx::user_clip_plane_op::less_than: - clip_enabled_flags[index] = 1; - clip_distance_factors[index] = -1.f; - break; - } - } - - memcpy(buffer, data_block, 2 * 8 * sizeof(u32)); - } - - /** - * Fill buffer with vertex program constants. - * Buffer must be at least 512 float4 wide. - */ - void thread::fill_vertex_program_constants_data(void* buffer, const std::span& reloc_table) - { - if (!reloc_table.empty()) [[ likely ]] - { - char* dst = reinterpret_cast(buffer); - for (const auto& index : reloc_table) - { - utils::stream_vector_from_memory(dst, &rsx::method_registers.transform_constants[index]); - dst += 16; - } - } - else - { - memcpy(buffer, rsx::method_registers.transform_constants.data(), 468 * 4 * sizeof(float)); - } - } - - void thread::fill_fragment_state_buffer(void* buffer, const RSXFragmentProgram& /*fragment_program*/) - { - ROP_control_t rop_control{}; - - if (rsx::method_registers.alpha_test_enabled()) - { - const u32 alpha_func = static_cast(rsx::method_registers.alpha_func()); - rop_control.set_alpha_test_func(alpha_func); - rop_control.enable_alpha_test(); - } - - if (rsx::method_registers.polygon_stipple_enabled()) - { - rop_control.enable_polygon_stipple(); - } - - if (rsx::method_registers.msaa_alpha_to_coverage_enabled() && !backend_config.supports_hw_a2c) - { - // TODO: Properly support alpha-to-coverage and alpha-to-one behavior in shaders - // Alpha values generate a coverage mask for order independent blending - // Requires hardware AA to work properly (or just fragment sample stage in fragment shaders) - // Simulated using combined alpha blend and alpha test - rop_control.enable_alpha_to_coverage(); - if (rsx::method_registers.msaa_sample_mask()) - { - rop_control.enable_MSAA_writes(); - } - - // Sample configuration bits - switch (rsx::method_registers.surface_antialias()) - { - case rsx::surface_antialiasing::center_1_sample: - break; - case rsx::surface_antialiasing::diagonal_centered_2_samples: - rop_control.set_msaa_control(1u); - break; - default: - rop_control.set_msaa_control(3u); - break; - } - } - - const f32 fog0 = rsx::method_registers.fog_params_0(); - const f32 fog1 = rsx::method_registers.fog_params_1(); - const u32 fog_mode = static_cast(rsx::method_registers.fog_equation()); - - // Check if framebuffer is actually an XRGB format and not a WZYX format - switch (rsx::method_registers.surface_color()) - { - case rsx::surface_color_format::w16z16y16x16: - case rsx::surface_color_format::w32z32y32x32: - case rsx::surface_color_format::x32: - // These behave very differently from "normal" formats. - break; - default: - // Integer framebuffer formats. - rop_control.enable_framebuffer_INT(); - - // Check if we want sRGB conversion. - if (rsx::method_registers.framebuffer_srgb_enabled()) - { - rop_control.enable_framebuffer_sRGB(); - } - break; - } - - // Generate wpos coefficients - // wpos equation is now as follows: - // wpos.y = (frag_coord / resolution_scale) * ((window_origin!=top)?-1.: 1.) + ((window_origin!=top)? window_height : 0) - // wpos.x = (frag_coord / resolution_scale) - // wpos.zw = frag_coord.zw - - const auto window_origin = rsx::method_registers.shader_window_origin(); - const u32 window_height = rsx::method_registers.shader_window_height(); - const f32 resolution_scale = (window_height <= static_cast(g_cfg.video.min_scalable_dimension)) ? 1.f : rsx::get_resolution_scale(); - const f32 wpos_scale = (window_origin == rsx::window_origin::top) ? (1.f / resolution_scale) : (-1.f / resolution_scale); - const f32 wpos_bias = (window_origin == rsx::window_origin::top) ? 0.f : window_height; - const f32 alpha_ref = rsx::method_registers.alpha_ref(); - - u32 *dst = static_cast(buffer); - utils::stream_vector(dst, std::bit_cast(fog0), std::bit_cast(fog1), rop_control.value, std::bit_cast(alpha_ref)); - utils::stream_vector(dst + 4, 0u, fog_mode, std::bit_cast(wpos_scale), std::bit_cast(wpos_bias)); - } - u64 thread::timestamp() { const u64 freq = sys_time_get_timebase_frequency(); diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index e120e3d57b..4724b43ecd 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -399,30 +399,6 @@ namespace rsx draw_command_processor& GRAPH_frontend() { return m_draw_processor; } - /** - * Fill buffer with 4x4 scale offset matrix. - * Vertex shader's position is to be multiplied by this matrix. - * if flip_y is set, the matrix is modified to use d3d convention. - */ - void fill_scale_offset_data(void *buffer, bool flip_y) const; - - /** - * Fill buffer with user clip information - */ - void fill_user_clip_data(void *buffer) const; - - /** - * Fill buffer with vertex program constants. - * Relocation table allows to do a partial fill with only selected registers. - */ - void fill_vertex_program_constants_data(void* buffer, const std::span& reloc_table); - - /** - * Fill buffer with fragment rasterization state. - * Fills current fog values, alpha test parameters and texture scaling parameters - */ - void fill_fragment_state_buffer(void* buffer, const RSXFragmentProgram& fragment_program); - /** * Notify that a section of memory has been mapped * If there is a notify_memory_unmapped request on this range yet to be handled, diff --git a/rpcs3/Emu/RSX/VK/VKGSRender.cpp b/rpcs3/Emu/RSX/VK/VKGSRender.cpp index 4fcef46b86..a43918b947 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRender.cpp +++ b/rpcs3/Emu/RSX/VK/VKGSRender.cpp @@ -2139,8 +2139,8 @@ void VKGSRender::load_program_env() const auto mem = m_vertex_env_ring_info.alloc<256>(256); auto buf = static_cast(m_vertex_env_ring_info.map(mem, 148)); - fill_scale_offset_data(buf, false); - fill_user_clip_data(buf + 64); + m_draw_processor.fill_scale_offset_data(buf, false); + m_draw_processor.fill_user_clip_data(buf + 64); *(reinterpret_cast(buf + 128)) = rsx::method_registers.transform_branch_bits(); *(reinterpret_cast(buf + 132)) = rsx::method_registers.point_size() * rsx::get_resolution_scale(); *(reinterpret_cast(buf + 136)) = rsx::method_registers.clip_min(); @@ -2200,7 +2200,7 @@ void VKGSRender::load_program_env() auto mem = m_fragment_env_ring_info.alloc<256>(256); auto buf = m_fragment_env_ring_info.map(mem, 32); - fill_fragment_state_buffer(buf, current_fragment_program); + m_draw_processor.fill_fragment_state_buffer(buf, current_fragment_program); m_fragment_env_ring_info.unmap(); m_fragment_env_buffer_info = { m_fragment_env_ring_info.heap->value, mem, 32 }; } @@ -2317,7 +2317,7 @@ void VKGSRender::upload_transform_constants(const rsx::io_buffer& buffer) const auto constant_ids = (transform_constants_size == 8192) ? std::span{} : std::span(m_vertex_prog->constant_ids); - fill_vertex_program_constants_data(buf, constant_ids); + m_draw_processor.fill_vertex_program_constants_data(buf, constant_ids); } }