#include "stdafx.h" #include "nv308a.h" #include "Emu/RSX/RSXThread.h" #include "Emu/RSX/Core/RSXReservationLock.hpp" #include "context_accessors.define.h" namespace rsx { namespace nv308a { void color::impl(context* ctx, u32 reg, u32) { const u32 out_x_max = REGS(ctx)->nv308a_size_out_x(); const u32 index = reg - NV308A_COLOR; if (index >= out_x_max) { // Skip return; } // Get position of the current command arg [[maybe_unused]] const u32 src_offset = RSX(ctx)->fifo_ctrl->get_pos(); // FIFO args count including this one const u32 fifo_args_cnt = RSX(ctx)->fifo_ctrl->get_remaining_args_count() + 1; // The range of methods this function resposible to const u32 method_range = std::min(0x700 - index, out_x_max - index); // Get limit imposed by FIFO PUT (if put is behind get it will result in a number ignored by min) const u32 fifo_read_limit = static_cast(((RSX(ctx)->ctrl->put & ~3ull) - (RSX(ctx)->fifo_ctrl->get_pos())) / 4); u32 count = std::min({fifo_args_cnt, fifo_read_limit, method_range}); if (!count) { rsx_log.error("nv308a::color - No data to read/write."); RSX(ctx)->fifo_ctrl->skip_methods(fifo_args_cnt - 1); return; } const u32 dst_dma = REGS(ctx)->blit_engine_output_location_nv3062(); const u32 dst_offset = REGS(ctx)->blit_engine_output_offset_nv3062(); const u32 out_pitch = REGS(ctx)->blit_engine_output_pitch_nv3062(); const u32 x = REGS(ctx)->nv308a_x() + index; const u32 y = REGS(ctx)->nv308a_y(); const auto fifo_span = RSX(ctx)->fifo_ctrl->get_current_arg_ptr(count); if (fifo_span.size() < count) { count = ::size32(fifo_span); } // Skip "handled methods" RSX(ctx)->fifo_ctrl->skip_methods(count - 1); // 308A::COLOR can be used to create custom sync primitives. // Hide this behind strict mode due to the potential performance implications. if (count == 1 && g_cfg.video.strict_rendering_mode && !g_cfg.video.relaxed_zcull_sync) { RSX(ctx)->sync(); } switch (*REGS(ctx)->blit_engine_nv3062_color_format()) { case blit_engine::transfer_destination_format::a8r8g8b8: case blit_engine::transfer_destination_format::y32: { // Bit cast - optimize to mem copy const u32 data_length = count * 4; const auto dst_address = get_address(dst_offset + (x * 4) + (out_pitch * y), dst_dma, data_length); if (!dst_address) { RSX(ctx)->recover_fifo(); return; } const auto dst = vm::_ptr(dst_address); const auto src = reinterpret_cast(fifo_span.data()); rsx::reservation_lock rsx_lock(dst_address, data_length); if (RSX(ctx)->fifo_ctrl->last_cmd() & RSX_METHOD_NON_INCREMENT_CMD_MASK) [[unlikely]] { // Move last 32 bits reinterpret_cast(dst)[0] = reinterpret_cast(src)[count - 1]; RSX(ctx)->invalidate_fragment_program(dst_dma, dst_offset, 4); } else { if (dst_dma & CELL_GCM_LOCATION_MAIN) { // May overlap std::memmove(dst, src, data_length); } else { // Never overlaps std::memcpy(dst, src, data_length); } RSX(ctx)->invalidate_fragment_program(dst_dma, dst_offset, count * 4); } break; } case blit_engine::transfer_destination_format::r5g6b5: { const auto data_length = count * 2; const auto dst_address = get_address(dst_offset + (x * 2) + (y * out_pitch), dst_dma, data_length); const auto dst = vm::_ptr(dst_address); const auto src = utils::bless>(fifo_span.data()); if (!dst_address) { RSX(ctx)->recover_fifo(); return; } rsx::reservation_lock rsx_lock(dst_address, data_length); auto convert = [](u32 input) -> u16 { // Input is considered to be ARGB8 u32 r = (input >> 16) & 0xFF; u32 g = (input >> 8) & 0xFF; u32 b = input & 0xFF; r = (r * 32) / 255; g = (g * 64) / 255; b = (b * 32) / 255; return static_cast((r << 11) | (g << 5) | b); }; if (RSX(ctx)->fifo_ctrl->last_cmd() & RSX_METHOD_NON_INCREMENT_CMD_MASK) [[unlikely]] { // Move last 16 bits dst[0] = convert(src[count - 1]); RSX(ctx)->invalidate_fragment_program(dst_dma, dst_offset, 2); break; } for (u32 i = 0; i < count; i++) { dst[i] = convert(src[i]); } RSX(ctx)->invalidate_fragment_program(dst_dma, dst_offset, count * 2); break; } default: { fmt::throw_exception("Unreachable"); } } } } // namespace nv308a } // namespace rsx