rpcs3/rpcs3/Emu/RSX/NV47/HW/nv308a.cpp

167 lines
4.7 KiB
C++

#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<u32>(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<u32>(((RSX(ctx)->ctrl->put & ~3ull) - (RSX(ctx)->fifo_ctrl->get_pos())) / 4);
u32 count = std::min<u32>({ 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<u8>(dst_address);
const auto src = reinterpret_cast<const u8*>(fifo_span.data());
rsx::reservation_lock<true> 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<u32*>(dst)[0] = reinterpret_cast<const u32*>(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<u16>(dst_address);
const auto src = utils::bless<const be_t<u32>>(fifo_span.data());
if (!dst_address)
{
RSX(ctx)->recover_fifo();
return;
}
rsx::reservation_lock<true> 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<u16>((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");
}
}
}
}
}