Merge branch 'swrenderer' into threaded-gpu
This commit is contained in:
commit
88852eb4c7
|
@ -129,10 +129,15 @@ bool CDImageCueSheet::OpenAndParse(const char* filename)
|
|||
track_length = file_size - track_start;
|
||||
}
|
||||
|
||||
// two seconds pregap for track 1 is assumed if not specified
|
||||
// Two seconds pregap for track 1 is assumed if not specified.
|
||||
// Some people have broken (older) dumps where a two second pregap was implicit but not specified in the cuesheet.
|
||||
// The problem is we can't tell between a missing implicit two second pregap and a zero second pregap. Most of these
|
||||
// seem to be a single bin file for all tracks. So if this is the case, we add the two seconds in if it's not
|
||||
// specified.
|
||||
long pregap_frames = track_get_zero_pre(track);
|
||||
bool pregap_in_file = pregap_frames > 0 && track_start >= pregap_frames;
|
||||
if (track_num == 1 && pregap_frames < 0)
|
||||
const bool pregap_in_file = pregap_frames > 0 && track_start >= pregap_frames;
|
||||
const bool is_multi_track_bin = (track_num > 1 && track_file_index == m_indices[0].file_index);
|
||||
if ((track_num == 1 || is_multi_track_bin) && pregap_frames < 0)
|
||||
pregap_frames = 2 * FRAMES_PER_SECOND;
|
||||
|
||||
// create the index for the pregap
|
||||
|
|
|
@ -1114,16 +1114,13 @@ bool FileSystem::DeleteDirectory(const char* Path, bool Recursive)
|
|||
|
||||
std::string GetProgramPath()
|
||||
{
|
||||
const HANDLE hProcess = GetCurrentProcess();
|
||||
|
||||
std::wstring buffer;
|
||||
buffer.resize(MAX_PATH);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
DWORD nChars = static_cast<DWORD>(buffer.size());
|
||||
if (!QueryFullProcessImageNameW(GetCurrentProcess(), 0, buffer.data(), &nChars) &&
|
||||
GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
||||
DWORD nChars = GetModuleFileNameW(nullptr, buffer.data(), static_cast<DWORD>(buffer.size()));
|
||||
if (nChars == static_cast<DWORD>(buffer.size()) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
||||
{
|
||||
buffer.resize(buffer.size() * 2);
|
||||
continue;
|
||||
|
@ -1315,7 +1312,7 @@ bool StatFile(const char* Path, FILESYSTEM_STAT_DATA* pStatData)
|
|||
if (Path[0] == '\0')
|
||||
return false;
|
||||
|
||||
// stat file
|
||||
// stat file
|
||||
#ifdef __HAIKU__
|
||||
struct stat sysStatData;
|
||||
if (stat(Path, &sysStatData) < 0)
|
||||
|
@ -1349,7 +1346,7 @@ bool FileExists(const char* Path)
|
|||
if (Path[0] == '\0')
|
||||
return false;
|
||||
|
||||
// stat file
|
||||
// stat file
|
||||
#ifdef __HAIKU__
|
||||
struct stat sysStatData;
|
||||
if (stat(Path, &sysStatData) < 0)
|
||||
|
@ -1371,13 +1368,13 @@ bool DirectoryExists(const char* Path)
|
|||
if (Path[0] == '\0')
|
||||
return false;
|
||||
|
||||
// stat file
|
||||
// stat file
|
||||
#ifdef __HAIKU__
|
||||
struct stat sysStatData;
|
||||
if (stat(Path, &sysStatData) < 0)
|
||||
struct stat sysStatData;
|
||||
if (stat(Path, &sysStatData) < 0)
|
||||
#else
|
||||
struct stat64 sysStatData;
|
||||
if (stat64(Path, &sysStatData) < 0)
|
||||
struct stat64 sysStatData;
|
||||
if (stat64(Path, &sysStatData) < 0)
|
||||
#endif
|
||||
return false;
|
||||
|
||||
|
|
|
@ -103,7 +103,10 @@ void GPU::SoftReset()
|
|||
m_blit_buffer.clear();
|
||||
m_blit_remaining_words = 0;
|
||||
m_draw_mode.bits = 0;
|
||||
m_texture_window.bits = 0;
|
||||
m_texture_window.and_x = 0xFF;
|
||||
m_texture_window.and_y = 0xFF;
|
||||
m_texture_window.or_x = 0x00;
|
||||
m_texture_window.or_y = 0x00;
|
||||
UpdateDMARequest();
|
||||
UpdateCRTCConfig();
|
||||
UpdateCRTCTickEvent();
|
||||
|
@ -120,14 +123,15 @@ bool GPU::DoState(StateWrapper& sw)
|
|||
|
||||
sw.Do(&m_GPUSTAT.bits);
|
||||
|
||||
sw.Do(&m_drawing_offset.x);
|
||||
sw.Do(&m_drawing_offset.y);
|
||||
sw.Do(&m_drawing_area.left);
|
||||
sw.Do(&m_drawing_area.top);
|
||||
sw.Do(&m_drawing_area.right);
|
||||
sw.Do(&m_drawing_area.bottom);
|
||||
sw.Do(&m_draw_mode.bits);
|
||||
sw.Do(&m_texture_window.bits);
|
||||
sw.Do(&m_texture_window.and_x);
|
||||
sw.Do(&m_texture_window.and_y);
|
||||
sw.Do(&m_texture_window.or_x);
|
||||
sw.Do(&m_texture_window.or_y);
|
||||
|
||||
sw.Do(&m_console_is_pal);
|
||||
sw.Do(&m_set_texture_disable_mask);
|
||||
|
@ -1029,8 +1033,9 @@ void GPU::HandleGetGPUInfoCommand(u32 value)
|
|||
|
||||
case 0x02: // Get Texture Window
|
||||
{
|
||||
// FIXME
|
||||
Log_DebugPrintf("Get texture window");
|
||||
m_GPUREAD_latch = m_texture_window.bits;
|
||||
m_GPUREAD_latch = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
@ -302,7 +302,7 @@ private:
|
|||
|
||||
Common::Rectangle<u32> m_drawing_area{0, 0, VRAM_WIDTH, VRAM_HEIGHT};
|
||||
GPUDrawModeReg m_draw_mode{};
|
||||
GPUTextureWindowReg m_texture_window{};
|
||||
GPUTextureWindow m_texture_window{};
|
||||
|
||||
bool m_console_is_pal = false;
|
||||
bool m_set_texture_disable_mask = false;
|
||||
|
|
|
@ -249,9 +249,16 @@ bool GPU::HandleSetTextureWindowCommand()
|
|||
{
|
||||
const u32 param = FifoPop() & 0x00FFFFFFu;
|
||||
|
||||
m_texture_window.bits = param;
|
||||
Log_DebugPrintf("Set texture window %02X %02X %02X %02X", m_texture_window.mask_x, m_texture_window.mask_y,
|
||||
m_texture_window.offset_x, m_texture_window.offset_y);
|
||||
const u8 mask_x = Truncate8(param & UINT32_C(0x1F));
|
||||
const u8 mask_y = Truncate8((param >> 5) & UINT32_C(0x1F));
|
||||
const u8 offset_x = Truncate8((param >> 10) & UINT32_C(0x1F));
|
||||
const u8 offset_y = Truncate8((param >> 15) & UINT32_C(0x1F));
|
||||
Log_DebugPrintf("Set texture window %02X %02X %02X %02X", mask_x, mask_y, offset_x, offset_y);
|
||||
|
||||
m_texture_window.and_x = ~(mask_x * 8);
|
||||
m_texture_window.and_y = ~(mask_y * 8);
|
||||
m_texture_window.or_x = (offset_x & mask_x) * 8u;
|
||||
m_texture_window.or_y = (offset_y & mask_y) * 8u;
|
||||
|
||||
AddCommandTicks(1);
|
||||
EndCommand();
|
||||
|
@ -371,7 +378,7 @@ void GPU::FillDrawCommand(GPUBackendDrawCommand* cmd, GPURenderCommand rc) const
|
|||
FillBackendCommandParameters(cmd);
|
||||
cmd->rc.bits = rc.bits;
|
||||
cmd->draw_mode.bits = m_draw_mode.bits;
|
||||
cmd->window.bits = m_texture_window.bits;
|
||||
cmd->window = m_texture_window;
|
||||
}
|
||||
|
||||
void GPU::UpdateDrawingArea()
|
||||
|
@ -576,7 +583,7 @@ bool GPU::HandleRenderRectangleCommand()
|
|||
FillDrawCommand(cmd, rc);
|
||||
cmd->color = rc.color_for_first_vertex;
|
||||
cmd->draw_mode.bits = m_draw_mode.bits;
|
||||
cmd->window.bits = m_texture_window.bits;
|
||||
cmd->window = m_texture_window;
|
||||
|
||||
const GPUVertexPosition vp{FifoPop()};
|
||||
cmd->x = TruncateGPUVertexPosition(m_drawing_offset.x + vp.x);
|
||||
|
|
|
@ -723,14 +723,13 @@ void GPU_HW::SetupDraw(const GPUBackendDrawCommand* cmd)
|
|||
m_batch.transparency_mode = transparency_mode;
|
||||
m_batch.dithering = dithering_enable;
|
||||
|
||||
if (m_last_texture_window_reg.bits != cmd->window.bits)
|
||||
if (std::memcmp(&m_last_texture_window, &cmd->window, sizeof(m_last_texture_window)) != 0)
|
||||
{
|
||||
m_last_texture_window_reg.bits = cmd->window.bits;
|
||||
|
||||
m_batch_ubo_data.u_texture_window_mask[0] = ZeroExtend32(cmd->window.mask_x.GetValue());
|
||||
m_batch_ubo_data.u_texture_window_mask[1] = ZeroExtend32(cmd->window.mask_y.GetValue());
|
||||
m_batch_ubo_data.u_texture_window_offset[0] = ZeroExtend32(cmd->window.offset_x.GetValue());
|
||||
m_batch_ubo_data.u_texture_window_offset[1] = ZeroExtend32(cmd->window.offset_y.GetValue());
|
||||
m_last_texture_window = cmd->window;
|
||||
m_batch_ubo_data.u_texture_window_and[0] = ZeroExtend32(cmd->window.and_x);
|
||||
m_batch_ubo_data.u_texture_window_and[1] = ZeroExtend32(cmd->window.and_y);
|
||||
m_batch_ubo_data.u_texture_window_or[0] = ZeroExtend32(cmd->window.or_x);
|
||||
m_batch_ubo_data.u_texture_window_or[1] = ZeroExtend32(cmd->window.or_y);
|
||||
m_batch_ubo_dirty = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,8 +111,8 @@ protected:
|
|||
|
||||
struct BatchUBOData
|
||||
{
|
||||
u32 u_texture_window_mask[2];
|
||||
u32 u_texture_window_offset[2];
|
||||
u32 u_texture_window_and[2];
|
||||
u32 u_texture_window_or[2];
|
||||
float u_src_alpha_factor;
|
||||
float u_dst_alpha_factor;
|
||||
u32 u_interlaced_displayed_field;
|
||||
|
@ -269,7 +269,7 @@ protected:
|
|||
Common::Rectangle<u32> m_vram_dirty_rect;
|
||||
|
||||
GPUDrawModeReg m_last_texture_page_bits{};
|
||||
GPUTextureWindowReg m_last_texture_window_reg{};
|
||||
GPUTextureWindow m_last_texture_window{};
|
||||
|
||||
// Statistics
|
||||
RendererStats m_renderer_stats = {};
|
||||
|
|
|
@ -68,7 +68,7 @@ float4 RGBA5551ToRGBA8(uint v)
|
|||
void GPU_HW_ShaderGen::WriteBatchUniformBuffer(std::stringstream& ss)
|
||||
{
|
||||
DeclareUniformBuffer(ss,
|
||||
{"uint2 u_texture_window_mask", "uint2 u_texture_window_offset", "float u_src_alpha_factor",
|
||||
{"uint2 u_texture_window_and", "uint2 u_texture_window_or", "float u_src_alpha_factor",
|
||||
"float u_dst_alpha_factor", "uint u_interlaced_displayed_field",
|
||||
"bool u_set_mask_while_drawing"},
|
||||
false);
|
||||
|
@ -698,16 +698,16 @@ CONSTANT float4 TRANSPARENT_PIXEL_COLOR = float4(0.0, 0.0, 0.0, 0.0);
|
|||
|
||||
uint2 ApplyTextureWindow(uint2 coords)
|
||||
{
|
||||
uint x = (uint(coords.x) & ~(u_texture_window_mask.x * 8u)) | ((u_texture_window_offset.x & u_texture_window_mask.x) * 8u);
|
||||
uint y = (uint(coords.y) & ~(u_texture_window_mask.y * 8u)) | ((u_texture_window_offset.y & u_texture_window_mask.y) * 8u);
|
||||
uint x = (uint(coords.x) & u_texture_window_and.x) | u_texture_window_or.x;
|
||||
uint y = (uint(coords.y) & u_texture_window_and.y) | u_texture_window_or.y;
|
||||
return uint2(x, y);
|
||||
}
|
||||
|
||||
uint2 ApplyUpscaledTextureWindow(uint2 coords)
|
||||
{
|
||||
uint x = (uint(coords.x) & ~(u_texture_window_mask.x * 8u * RESOLUTION_SCALE)) | ((u_texture_window_offset.x & u_texture_window_mask.x) * 8u * RESOLUTION_SCALE);
|
||||
uint y = (uint(coords.y) & ~(u_texture_window_mask.y * 8u * RESOLUTION_SCALE)) | ((u_texture_window_offset.y & u_texture_window_mask.y) * 8u * RESOLUTION_SCALE);
|
||||
return uint2(x, y);
|
||||
uint2 native_coords = coords / uint2(RESOLUTION_SCALE, RESOLUTION_SCALE);
|
||||
uint2 coords_offset = coords % uint2(RESOLUTION_SCALE, RESOLUTION_SCALE);
|
||||
return (ApplyTextureWindow(native_coords) * uint2(RESOLUTION_SCALE, RESOLUTION_SCALE)) + coords_offset;
|
||||
}
|
||||
|
||||
uint2 FloatToIntegerCoords(float2 coords)
|
||||
|
|
|
@ -283,210 +283,6 @@ void GPU_SW::DrawLine(const GPUBackendDrawLineCommand* cmd)
|
|||
(this->*DrawFunction)(cmd, &cmd->vertices[i - 1], &cmd->vertices[i]);
|
||||
}
|
||||
|
||||
enum : u32
|
||||
{
|
||||
COORD_FRAC_BITS = 32,
|
||||
COLOR_FRAC_BITS = 12
|
||||
};
|
||||
|
||||
using FixedPointCoord = u64;
|
||||
|
||||
constexpr FixedPointCoord IntToFixedCoord(s32 x)
|
||||
{
|
||||
return (ZeroExtend64(static_cast<u32>(x)) << COORD_FRAC_BITS) | (ZeroExtend64(1u) << (COORD_FRAC_BITS - 1));
|
||||
}
|
||||
|
||||
using FixedPointColor = u32;
|
||||
|
||||
constexpr FixedPointColor IntToFixedColor(u8 r)
|
||||
{
|
||||
return ZeroExtend32(r) << COLOR_FRAC_BITS | (1u << (COLOR_FRAC_BITS - 1));
|
||||
}
|
||||
|
||||
constexpr u8 FixedColorToInt(FixedPointColor r)
|
||||
{
|
||||
return Truncate8(r >> 12);
|
||||
}
|
||||
|
||||
bool GPU_SW::IsClockwiseWinding(const GPUBackendDrawPolygonCommand::Vertex* v0,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v1,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v2)
|
||||
{
|
||||
const s32 abx = v1->x - v0->x;
|
||||
const s32 aby = v1->y - v0->y;
|
||||
const s32 acx = v2->x - v0->x;
|
||||
const s32 acy = v2->y - v0->y;
|
||||
return ((abx * acy) - (aby * acx) < 0);
|
||||
}
|
||||
|
||||
static constexpr bool IsTopLeftEdge(s32 ex, s32 ey)
|
||||
{
|
||||
return (ey < 0 || (ey == 0 && ex < 0));
|
||||
}
|
||||
|
||||
static constexpr u8 Interpolate(u8 v0, u8 v1, u8 v2, s32 w0, s32 w1, s32 w2, s32 ws, s32 half_ws)
|
||||
{
|
||||
const s32 v = w0 * static_cast<s32>(static_cast<u32>(v0)) + w1 * static_cast<s32>(static_cast<u32>(v1)) +
|
||||
w2 * static_cast<s32>(static_cast<u32>(v2));
|
||||
const s32 vd = (v + half_ws) / ws;
|
||||
return (vd < 0) ? 0 : ((vd > 0xFF) ? 0xFF : static_cast<u8>(vd));
|
||||
}
|
||||
|
||||
template<bool shading_enable, bool texture_enable, bool raw_texture_enable, bool transparency_enable,
|
||||
bool dithering_enable>
|
||||
void GPU_SW::DrawTriangle(const GPUBackendDrawPolygonCommand* cmd, const GPUBackendDrawPolygonCommand::Vertex* v0,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v1,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v2)
|
||||
{
|
||||
#define orient2d(ax, ay, bx, by, cx, cy) ((bx - ax) * (cy - ay) - (by - ay) * (cx - ax))
|
||||
|
||||
// ensure the vertices follow a counter-clockwise order
|
||||
if (IsClockwiseWinding(v0, v1, v2))
|
||||
std::swap(v1, v2);
|
||||
|
||||
const s32 px0 = v0->x;
|
||||
const s32 py0 = v0->y;
|
||||
const s32 px1 = v1->x;
|
||||
const s32 py1 = v1->y;
|
||||
const s32 px2 = v2->x;
|
||||
const s32 py2 = v2->y;
|
||||
|
||||
// Barycentric coordinates at minX/minY corner
|
||||
const s32 ws = orient2d(px0, py0, px1, py1, px2, py2);
|
||||
const s32 half_ws = std::max<s32>((ws / 2) - 1, 0);
|
||||
if (ws == 0)
|
||||
return;
|
||||
|
||||
// compute bounding box of triangle
|
||||
s32 min_x = std::min(px0, std::min(px1, px2));
|
||||
s32 max_x = std::max(px0, std::max(px1, px2));
|
||||
s32 min_y = std::min(py0, std::min(py1, py2));
|
||||
s32 max_y = std::max(py0, std::max(py1, py2));
|
||||
|
||||
// clip to drawing area
|
||||
min_x = std::clamp(min_x, static_cast<s32>(m_drawing_area.left), static_cast<s32>(m_drawing_area.right));
|
||||
max_x = std::clamp(max_x, static_cast<s32>(m_drawing_area.left), static_cast<s32>(m_drawing_area.right));
|
||||
min_y = std::clamp(min_y, static_cast<s32>(m_drawing_area.top), static_cast<s32>(m_drawing_area.bottom));
|
||||
max_y = std::clamp(max_y, static_cast<s32>(m_drawing_area.top), static_cast<s32>(m_drawing_area.bottom));
|
||||
|
||||
// compute per-pixel increments
|
||||
const s32 a01 = py0 - py1, b01 = px1 - px0;
|
||||
const s32 a12 = py1 - py2, b12 = px2 - px1;
|
||||
const s32 a20 = py2 - py0, b20 = px0 - px2;
|
||||
|
||||
// top-left edge rule
|
||||
const s32 w0_bias = 0 - s32(IsTopLeftEdge(b12, a12));
|
||||
const s32 w1_bias = 0 - s32(IsTopLeftEdge(b20, a20));
|
||||
const s32 w2_bias = 0 - s32(IsTopLeftEdge(b01, a01));
|
||||
|
||||
// compute base barycentric coordinates
|
||||
s32 w0 = orient2d(px1, py1, px2, py2, min_x, min_y);
|
||||
s32 w1 = orient2d(px2, py2, px0, py0, min_x, min_y);
|
||||
s32 w2 = orient2d(px0, py0, px1, py1, min_x, min_y);
|
||||
|
||||
// *exclusive* of max coordinate in PSX
|
||||
for (s32 y = min_y; y <= max_y; y++)
|
||||
{
|
||||
s32 row_w0 = w0;
|
||||
s32 row_w1 = w1;
|
||||
s32 row_w2 = w2;
|
||||
|
||||
for (s32 x = min_x; x <= max_x; x++)
|
||||
{
|
||||
if (((row_w0 + w0_bias) | (row_w1 + w1_bias) | (row_w2 + w2_bias)) >= 0)
|
||||
{
|
||||
const s32 b0 = row_w0;
|
||||
const s32 b1 = row_w1;
|
||||
const s32 b2 = row_w2;
|
||||
|
||||
const u8 r =
|
||||
shading_enable ? Interpolate(v0->GetR(), v1->GetR(), v2->GetR(), b0, b1, b2, ws, half_ws) : v0->GetR();
|
||||
const u8 g =
|
||||
shading_enable ? Interpolate(v0->GetG(), v1->GetG(), v2->GetG(), b0, b1, b2, ws, half_ws) : v0->GetG();
|
||||
const u8 b =
|
||||
shading_enable ? Interpolate(v0->GetB(), v1->GetB(), v2->GetB(), b0, b1, b2, ws, half_ws) : v0->GetB();
|
||||
|
||||
const u8 u = texture_enable ? Interpolate(v0->GetU(), v1->GetU(), v2->GetU(), b0, b1, b2, ws, half_ws) : 0;
|
||||
const u8 v = texture_enable ? Interpolate(v0->GetV(), v1->GetV(), v2->GetV(), b0, b1, b2, ws, half_ws) : 0;
|
||||
|
||||
ShadePixel<texture_enable, raw_texture_enable, transparency_enable, dithering_enable>(
|
||||
cmd, static_cast<u32>(x), static_cast<u32>(y), r, g, b, u, v);
|
||||
}
|
||||
|
||||
row_w0 += a12;
|
||||
row_w1 += a20;
|
||||
row_w2 += a01;
|
||||
}
|
||||
|
||||
w0 += b12;
|
||||
w1 += b20;
|
||||
w2 += b01;
|
||||
}
|
||||
|
||||
#undef orient2d
|
||||
}
|
||||
|
||||
GPU_SW::DrawTriangleFunction GPU_SW::GetDrawTriangleFunction(bool shading_enable, bool texture_enable,
|
||||
bool raw_texture_enable, bool transparency_enable,
|
||||
bool dithering_enable)
|
||||
{
|
||||
#define F(SHADING, TEXTURE, RAW_TEXTURE, TRANSPARENCY, DITHERING) \
|
||||
&GPU_SW::DrawTriangle<SHADING, TEXTURE, RAW_TEXTURE, TRANSPARENCY, DITHERING>
|
||||
|
||||
static constexpr DrawTriangleFunction funcs[2][2][2][2][2] = {
|
||||
{{{{F(false, false, false, false, false), F(false, false, false, false, true)},
|
||||
{F(false, false, false, true, false), F(false, false, false, true, true)}},
|
||||
{{F(false, false, true, false, false), F(false, false, true, false, true)},
|
||||
{F(false, false, true, true, false), F(false, false, true, true, true)}}},
|
||||
{{{F(false, true, false, false, false), F(false, true, false, false, true)},
|
||||
{F(false, true, false, true, false), F(false, true, false, true, true)}},
|
||||
{{F(false, true, true, false, false), F(false, true, true, false, true)},
|
||||
{F(false, true, true, true, false), F(false, true, true, true, true)}}}},
|
||||
{{{{F(true, false, false, false, false), F(true, false, false, false, true)},
|
||||
{F(true, false, false, true, false), F(true, false, false, true, true)}},
|
||||
{{F(true, false, true, false, false), F(true, false, true, false, true)},
|
||||
{F(true, false, true, true, false), F(true, false, true, true, true)}}},
|
||||
{{{F(true, true, false, false, false), F(true, true, false, false, true)},
|
||||
{F(true, true, false, true, false), F(true, true, false, true, true)}},
|
||||
{{F(true, true, true, false, false), F(true, true, true, false, true)},
|
||||
{F(true, true, true, true, false), F(true, true, true, true, true)}}}}};
|
||||
|
||||
#undef F
|
||||
|
||||
return funcs[u8(shading_enable)][u8(texture_enable)][u8(raw_texture_enable)][u8(transparency_enable)]
|
||||
[u8(dithering_enable)];
|
||||
}
|
||||
|
||||
template<bool texture_enable, bool raw_texture_enable, bool transparency_enable>
|
||||
void GPU_SW::DrawRectangle(const GPUBackendDrawRectangleCommand* cmd)
|
||||
{
|
||||
const s32 origin_x = cmd->x;
|
||||
const s32 origin_y = cmd->y;
|
||||
const auto [r, g, b] = UnpackColorRGB24(cmd->color);
|
||||
const auto [origin_texcoord_x, origin_texcoord_y] = UnpackTexcoord(cmd->texcoord);
|
||||
|
||||
for (u32 offset_y = 0; offset_y < cmd->height; offset_y++)
|
||||
{
|
||||
const s32 y = origin_y + static_cast<s32>(offset_y);
|
||||
if (y < static_cast<s32>(m_drawing_area.top) || y > static_cast<s32>(m_drawing_area.bottom))
|
||||
continue;
|
||||
|
||||
const u8 texcoord_y = Truncate8(ZeroExtend32(origin_texcoord_y) + offset_y);
|
||||
|
||||
for (u32 offset_x = 0; offset_x < cmd->width; offset_x++)
|
||||
{
|
||||
const s32 x = origin_x + static_cast<s32>(offset_x);
|
||||
if (x < static_cast<s32>(m_drawing_area.left) || x > static_cast<s32>(m_drawing_area.right))
|
||||
continue;
|
||||
|
||||
const u8 texcoord_x = Truncate8(ZeroExtend32(origin_texcoord_x) + offset_x);
|
||||
|
||||
ShadePixel<texture_enable, raw_texture_enable, transparency_enable, false>(
|
||||
cmd, static_cast<u32>(x), static_cast<u32>(y), r, g, b, texcoord_x, texcoord_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr GPU_SW::DitherLUT GPU_SW::ComputeDitherLUT()
|
||||
{
|
||||
DitherLUT lut = {};
|
||||
|
@ -507,8 +303,8 @@ constexpr GPU_SW::DitherLUT GPU_SW::ComputeDitherLUT()
|
|||
static constexpr GPU_SW::DitherLUT s_dither_lut = GPU_SW::ComputeDitherLUT();
|
||||
|
||||
template<bool texture_enable, bool raw_texture_enable, bool transparency_enable, bool dithering_enable>
|
||||
void GPU_SW::ShadePixel(const GPUBackendDrawCommand* cmd, u32 x, u32 y, u8 color_r, u8 color_g, u8 color_b,
|
||||
u8 texcoord_x, u8 texcoord_y)
|
||||
void ALWAYS_INLINE_RELEASE GPU_SW::ShadePixel(const GPUBackendDrawCommand* cmd, u32 x, u32 y, u8 color_r, u8 color_g,
|
||||
u8 color_b, u8 texcoord_x, u8 texcoord_y)
|
||||
{
|
||||
VRAMPixel color;
|
||||
bool transparent;
|
||||
|
@ -516,8 +312,8 @@ void GPU_SW::ShadePixel(const GPUBackendDrawCommand* cmd, u32 x, u32 y, u8 color
|
|||
{
|
||||
// Apply texture window
|
||||
// TODO: Precompute the second half
|
||||
texcoord_x = (texcoord_x & ~(cmd->window.mask_x * 8u)) | ((cmd->window.offset_x & cmd->window.mask_x) * 8u);
|
||||
texcoord_y = (texcoord_y & ~(cmd->window.mask_y * 8u)) | ((cmd->window.offset_y & cmd->window.mask_y) * 8u);
|
||||
texcoord_x = (texcoord_x & cmd->window.and_x) | cmd->window.or_x;
|
||||
texcoord_y = (texcoord_y & cmd->window.and_y) | cmd->window.or_y;
|
||||
|
||||
VRAMPixel texture_color;
|
||||
switch (cmd->draw_mode.texture_mode)
|
||||
|
@ -636,105 +432,531 @@ void GPU_SW::ShadePixel(const GPUBackendDrawCommand* cmd, u32 x, u32 y, u8 color
|
|||
if ((bg_color.bits & mask_and) != 0)
|
||||
return;
|
||||
|
||||
if (cmd->params.interlaced_rendering && cmd->params.active_line_lsb == (Truncate8(static_cast<u32>(y)) & 1u))
|
||||
return;
|
||||
|
||||
SetPixel(static_cast<u32>(x), static_cast<u32>(y), color.bits | cmd->params.GetMaskOR());
|
||||
}
|
||||
|
||||
constexpr FixedPointCoord GetLineCoordStep(s32 delta, s32 k)
|
||||
template<bool texture_enable, bool raw_texture_enable, bool transparency_enable>
|
||||
void GPU_SW::DrawRectangle(const GPUBackendDrawRectangleCommand* cmd)
|
||||
{
|
||||
s64 delta_fp = static_cast<s64>(ZeroExtend64(static_cast<u32>(delta)) << 32);
|
||||
if (delta_fp < 0)
|
||||
delta_fp -= s64(k - 1);
|
||||
if (delta_fp > 0)
|
||||
delta_fp += s64(k - 1);
|
||||
const s32 origin_x = cmd->x;
|
||||
const s32 origin_y = cmd->y;
|
||||
const auto [r, g, b] = UnpackColorRGB24(cmd->color);
|
||||
const auto [origin_texcoord_x, origin_texcoord_y] = UnpackTexcoord(cmd->texcoord);
|
||||
|
||||
return static_cast<FixedPointCoord>(delta_fp / k);
|
||||
for (u32 offset_y = 0; offset_y < cmd->height; offset_y++)
|
||||
{
|
||||
const s32 y = origin_y + static_cast<s32>(offset_y);
|
||||
if (y < static_cast<s32>(m_drawing_area.top) || y > static_cast<s32>(m_drawing_area.bottom) ||
|
||||
(cmd->params.interlaced_rendering && cmd->params.active_line_lsb == (Truncate8(static_cast<u32>(y)) & 1u)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const u8 texcoord_y = Truncate8(ZeroExtend32(origin_texcoord_y) + offset_y);
|
||||
|
||||
for (u32 offset_x = 0; offset_x < cmd->width; offset_x++)
|
||||
{
|
||||
const s32 x = origin_x + static_cast<s32>(offset_x);
|
||||
if (x < static_cast<s32>(m_drawing_area.left) || x > static_cast<s32>(m_drawing_area.right))
|
||||
continue;
|
||||
|
||||
const u8 texcoord_x = Truncate8(ZeroExtend32(origin_texcoord_x) + offset_x);
|
||||
|
||||
ShadePixel<texture_enable, raw_texture_enable, transparency_enable, false>(
|
||||
cmd, static_cast<u32>(x), static_cast<u32>(y), r, g, b, texcoord_x, texcoord_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr s32 FixedToIntCoord(FixedPointCoord x)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Polygon and line rasterization ported from Mednafen
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define COORD_FBS 12
|
||||
#define COORD_MF_INT(n) ((n) << COORD_FBS)
|
||||
#define COORD_POST_PADDING 12
|
||||
|
||||
static ALWAYS_INLINE_RELEASE s64 MakePolyXFP(s32 x)
|
||||
{
|
||||
return static_cast<s32>(Truncate32(x >> COORD_FRAC_BITS));
|
||||
return ((u64)x << 32) + ((1ULL << 32) - (1 << 11));
|
||||
}
|
||||
|
||||
constexpr FixedPointColor GetLineColorStep(s32 delta, s32 k)
|
||||
static ALWAYS_INLINE_RELEASE s64 MakePolyXFPStep(s32 dx, s32 dy)
|
||||
{
|
||||
return static_cast<s32>(static_cast<u32>(delta) << COLOR_FRAC_BITS) / k;
|
||||
s64 ret;
|
||||
s64 dx_ex = (u64)dx << 32;
|
||||
|
||||
if (dx_ex < 0)
|
||||
dx_ex -= dy - 1;
|
||||
|
||||
if (dx_ex > 0)
|
||||
dx_ex += dy - 1;
|
||||
|
||||
ret = dx_ex / dy;
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
static ALWAYS_INLINE_RELEASE s32 GetPolyXFP_Int(s64 xfp)
|
||||
{
|
||||
return (xfp >> 32);
|
||||
}
|
||||
|
||||
template<bool shading_enable, bool texture_enable>
|
||||
bool ALWAYS_INLINE_RELEASE GPU_SW::CalcIDeltas(i_deltas& idl, const GPUBackendDrawPolygonCommand::Vertex* A,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* B,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* C)
|
||||
{
|
||||
#define CALCIS(x, y) (((B->x - A->x) * (C->y - B->y)) - ((C->x - B->x) * (B->y - A->y)))
|
||||
|
||||
s32 denom = CALCIS(x, y);
|
||||
|
||||
if (!denom)
|
||||
return false;
|
||||
|
||||
if constexpr (shading_enable)
|
||||
{
|
||||
idl.dr_dx = (u32)(CALCIS(r, y) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
idl.dr_dy = (u32)(CALCIS(x, r) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
|
||||
idl.dg_dx = (u32)(CALCIS(g, y) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
idl.dg_dy = (u32)(CALCIS(x, g) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
|
||||
idl.db_dx = (u32)(CALCIS(b, y) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
idl.db_dy = (u32)(CALCIS(x, b) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
}
|
||||
|
||||
if constexpr (texture_enable)
|
||||
{
|
||||
idl.du_dx = (u32)(CALCIS(u, y) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
idl.du_dy = (u32)(CALCIS(x, u) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
|
||||
idl.dv_dx = (u32)(CALCIS(v, y) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
idl.dv_dy = (u32)(CALCIS(x, v) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
#undef CALCIS
|
||||
}
|
||||
|
||||
template<bool shading_enable, bool texture_enable>
|
||||
void ALWAYS_INLINE_RELEASE GPU_SW::AddIDeltas_DX(i_group& ig, const i_deltas& idl, u32 count /*= 1*/)
|
||||
{
|
||||
if constexpr (shading_enable)
|
||||
{
|
||||
ig.r += idl.dr_dx * count;
|
||||
ig.g += idl.dg_dx * count;
|
||||
ig.b += idl.db_dx * count;
|
||||
}
|
||||
|
||||
if constexpr (texture_enable)
|
||||
{
|
||||
ig.u += idl.du_dx * count;
|
||||
ig.v += idl.dv_dx * count;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool shading_enable, bool texture_enable>
|
||||
void ALWAYS_INLINE_RELEASE GPU_SW::AddIDeltas_DY(i_group& ig, const i_deltas& idl, u32 count /*= 1*/)
|
||||
{
|
||||
if constexpr (shading_enable)
|
||||
{
|
||||
ig.r += idl.dr_dy * count;
|
||||
ig.g += idl.dg_dy * count;
|
||||
ig.b += idl.db_dy * count;
|
||||
}
|
||||
|
||||
if constexpr (texture_enable)
|
||||
{
|
||||
ig.u += idl.du_dy * count;
|
||||
ig.v += idl.dv_dy * count;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool shading_enable, bool texture_enable, bool raw_texture_enable, bool transparency_enable,
|
||||
bool dithering_enable>
|
||||
void GPU_SW::DrawSpan(const GPUBackendDrawPolygonCommand* cmd, s32 y, s32 x_start, s32 x_bound, i_group ig,
|
||||
const i_deltas& idl)
|
||||
{
|
||||
if (cmd->params.interlaced_rendering && cmd->params.active_line_lsb == (Truncate8(static_cast<u32>(y)) & 1u))
|
||||
return;
|
||||
|
||||
s32 x_ig_adjust = x_start;
|
||||
s32 w = x_bound - x_start;
|
||||
s32 x = TruncateGPUVertexPosition(x_start);
|
||||
|
||||
if (x < static_cast<s32>(m_drawing_area.left))
|
||||
{
|
||||
s32 delta = static_cast<s32>(m_drawing_area.left) - x;
|
||||
x_ig_adjust += delta;
|
||||
x += delta;
|
||||
w -= delta;
|
||||
}
|
||||
|
||||
if ((x + w) > (static_cast<s32>(m_drawing_area.right) + 1))
|
||||
w = static_cast<s32>(m_drawing_area.right) + 1 - x;
|
||||
|
||||
if (w <= 0)
|
||||
return;
|
||||
|
||||
AddIDeltas_DX<shading_enable, texture_enable>(ig, idl, x_ig_adjust);
|
||||
AddIDeltas_DY<shading_enable, texture_enable>(ig, idl, y);
|
||||
|
||||
do
|
||||
{
|
||||
const u32 r = ig.r >> (COORD_FBS + COORD_POST_PADDING);
|
||||
const u32 g = ig.g >> (COORD_FBS + COORD_POST_PADDING);
|
||||
const u32 b = ig.b >> (COORD_FBS + COORD_POST_PADDING);
|
||||
const u32 u = ig.u >> (COORD_FBS + COORD_POST_PADDING);
|
||||
const u32 v = ig.v >> (COORD_FBS + COORD_POST_PADDING);
|
||||
|
||||
ShadePixel<texture_enable, raw_texture_enable, transparency_enable, dithering_enable>(cmd,
|
||||
static_cast<u32>(x), static_cast<u32>(y), Truncate8(r), Truncate8(g), Truncate8(b), Truncate8(u), Truncate8(v));
|
||||
|
||||
x++;
|
||||
AddIDeltas_DX<shading_enable, texture_enable>(ig, idl);
|
||||
} while (--w > 0);
|
||||
}
|
||||
|
||||
template<bool shading_enable, bool texture_enable, bool raw_texture_enable, bool transparency_enable,
|
||||
bool dithering_enable>
|
||||
void GPU_SW::DrawTriangle(const GPUBackendDrawPolygonCommand* cmd, const GPUBackendDrawPolygonCommand::Vertex* v0,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v1,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v2)
|
||||
{
|
||||
u32 core_vertex;
|
||||
{
|
||||
u32 cvtemp = 0;
|
||||
|
||||
if (v1->x <= v0->x)
|
||||
{
|
||||
if (v2->x <= v1->x)
|
||||
cvtemp = (1 << 2);
|
||||
else
|
||||
cvtemp = (1 << 1);
|
||||
}
|
||||
else if (v2->x < v0->x)
|
||||
cvtemp = (1 << 2);
|
||||
else
|
||||
cvtemp = (1 << 0);
|
||||
|
||||
if (v2->y < v1->y)
|
||||
{
|
||||
std::swap(v2, v1);
|
||||
cvtemp = ((cvtemp >> 1) & 0x2) | ((cvtemp << 1) & 0x4) | (cvtemp & 0x1);
|
||||
}
|
||||
|
||||
if (v1->y < v0->y)
|
||||
{
|
||||
std::swap(v1, v0);
|
||||
cvtemp = ((cvtemp >> 1) & 0x1) | ((cvtemp << 1) & 0x2) | (cvtemp & 0x4);
|
||||
}
|
||||
|
||||
if (v2->y < v1->y)
|
||||
{
|
||||
std::swap(v2, v1);
|
||||
cvtemp = ((cvtemp >> 1) & 0x2) | ((cvtemp << 1) & 0x4) | (cvtemp & 0x1);
|
||||
}
|
||||
|
||||
core_vertex = cvtemp >> 1;
|
||||
}
|
||||
|
||||
if (v0->y == v2->y)
|
||||
return;
|
||||
|
||||
if (static_cast<u32>(std::abs(v2->x - v0->x)) >= MAX_PRIMITIVE_WIDTH ||
|
||||
static_cast<u32>(std::abs(v2->x - v1->x)) >= MAX_PRIMITIVE_WIDTH ||
|
||||
static_cast<u32>(std::abs(v1->x - v0->x)) >= MAX_PRIMITIVE_WIDTH ||
|
||||
static_cast<u32>(v2->y - v0->y) >= MAX_PRIMITIVE_HEIGHT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s64 base_coord = MakePolyXFP(v0->x);
|
||||
s64 base_step = MakePolyXFPStep((v2->x - v0->x), (v2->y - v0->y));
|
||||
s64 bound_coord_us;
|
||||
s64 bound_coord_ls;
|
||||
bool right_facing;
|
||||
|
||||
if (v1->y == v0->y)
|
||||
{
|
||||
bound_coord_us = 0;
|
||||
right_facing = (bool)(v1->x > v0->x);
|
||||
}
|
||||
else
|
||||
{
|
||||
bound_coord_us = MakePolyXFPStep((v1->x - v0->x), (v1->y - v0->y));
|
||||
right_facing = (bool)(bound_coord_us > base_step);
|
||||
}
|
||||
|
||||
if (v2->y == v1->y)
|
||||
bound_coord_ls = 0;
|
||||
else
|
||||
bound_coord_ls = MakePolyXFPStep((v2->x - v1->x), (v2->y - v1->y));
|
||||
|
||||
i_deltas idl;
|
||||
if (!CalcIDeltas<shading_enable, texture_enable>(idl, v0, v1, v2))
|
||||
return;
|
||||
|
||||
const GPUBackendDrawPolygonCommand::Vertex* vertices[3] = {v0, v1, v2};
|
||||
|
||||
i_group ig;
|
||||
if constexpr (texture_enable)
|
||||
{
|
||||
ig.u = (COORD_MF_INT(vertices[core_vertex]->u) + (1 << (COORD_FBS - 1))) << COORD_POST_PADDING;
|
||||
ig.v = (COORD_MF_INT(vertices[core_vertex]->v) + (1 << (COORD_FBS - 1))) << COORD_POST_PADDING;
|
||||
}
|
||||
|
||||
ig.r = (COORD_MF_INT(vertices[core_vertex]->r) + (1 << (COORD_FBS - 1))) << COORD_POST_PADDING;
|
||||
ig.g = (COORD_MF_INT(vertices[core_vertex]->g) + (1 << (COORD_FBS - 1))) << COORD_POST_PADDING;
|
||||
ig.b = (COORD_MF_INT(vertices[core_vertex]->b) + (1 << (COORD_FBS - 1))) << COORD_POST_PADDING;
|
||||
|
||||
AddIDeltas_DX<shading_enable, texture_enable>(ig, idl, -vertices[core_vertex]->x);
|
||||
AddIDeltas_DY<shading_enable, texture_enable>(ig, idl, -vertices[core_vertex]->y);
|
||||
|
||||
struct TriangleHalf
|
||||
{
|
||||
u64 x_coord[2];
|
||||
u64 x_step[2];
|
||||
|
||||
s32 y_coord;
|
||||
s32 y_bound;
|
||||
|
||||
bool dec_mode;
|
||||
} tripart[2];
|
||||
|
||||
u32 vo = 0;
|
||||
u32 vp = 0;
|
||||
if (core_vertex != 0)
|
||||
vo = 1;
|
||||
if (core_vertex == 2)
|
||||
vp = 3;
|
||||
|
||||
{
|
||||
TriangleHalf* tp = &tripart[vo];
|
||||
tp->y_coord = vertices[0 ^ vo]->y;
|
||||
tp->y_bound = vertices[1 ^ vo]->y;
|
||||
tp->x_coord[right_facing] = MakePolyXFP(vertices[0 ^ vo]->x);
|
||||
tp->x_step[right_facing] = bound_coord_us;
|
||||
tp->x_coord[!right_facing] = base_coord + ((vertices[vo]->y - vertices[0]->y) * base_step);
|
||||
tp->x_step[!right_facing] = base_step;
|
||||
tp->dec_mode = vo;
|
||||
}
|
||||
|
||||
{
|
||||
TriangleHalf* tp = &tripart[vo ^ 1];
|
||||
tp->y_coord = vertices[1 ^ vp]->y;
|
||||
tp->y_bound = vertices[2 ^ vp]->y;
|
||||
tp->x_coord[right_facing] = MakePolyXFP(vertices[1 ^ vp]->x);
|
||||
tp->x_step[right_facing] = bound_coord_ls;
|
||||
tp->x_coord[!right_facing] =
|
||||
base_coord + ((vertices[1 ^ vp]->y - vertices[0]->y) *
|
||||
base_step); // base_coord + ((vertices[1].y - vertices[0].y) * base_step);
|
||||
tp->x_step[!right_facing] = base_step;
|
||||
tp->dec_mode = vp;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < 2; i++)
|
||||
{
|
||||
s32 yi = tripart[i].y_coord;
|
||||
s32 yb = tripart[i].y_bound;
|
||||
|
||||
u64 lc = tripart[i].x_coord[0];
|
||||
u64 ls = tripart[i].x_step[0];
|
||||
|
||||
u64 rc = tripart[i].x_coord[1];
|
||||
u64 rs = tripart[i].x_step[1];
|
||||
|
||||
if (tripart[i].dec_mode)
|
||||
{
|
||||
while (yi > yb)
|
||||
{
|
||||
yi--;
|
||||
lc -= ls;
|
||||
rc -= rs;
|
||||
|
||||
s32 y = TruncateGPUVertexPosition(yi);
|
||||
|
||||
if (y < static_cast<s32>(m_drawing_area.top))
|
||||
break;
|
||||
|
||||
if (y > static_cast<s32>(m_drawing_area.bottom))
|
||||
continue;
|
||||
|
||||
DrawSpan<shading_enable, texture_enable, raw_texture_enable, transparency_enable, dithering_enable>(
|
||||
cmd, yi, GetPolyXFP_Int(lc), GetPolyXFP_Int(rc), ig, idl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (yi < yb)
|
||||
{
|
||||
s32 y = TruncateGPUVertexPosition(yi);
|
||||
|
||||
if (y > static_cast<s32>(m_drawing_area.bottom))
|
||||
break;
|
||||
|
||||
if (y >= static_cast<s32>(m_drawing_area.top))
|
||||
{
|
||||
|
||||
DrawSpan<shading_enable, texture_enable, raw_texture_enable, transparency_enable, dithering_enable>(
|
||||
cmd, yi, GetPolyXFP_Int(lc), GetPolyXFP_Int(rc), ig, idl);
|
||||
}
|
||||
|
||||
yi++;
|
||||
lc += ls;
|
||||
rc += rs;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GPU_SW::DrawTriangleFunction GPU_SW::GetDrawTriangleFunction(bool shading_enable, bool texture_enable,
|
||||
bool raw_texture_enable, bool transparency_enable,
|
||||
bool dithering_enable)
|
||||
{
|
||||
#define F(SHADING, TEXTURE, RAW_TEXTURE, TRANSPARENCY, DITHERING) \
|
||||
&GPU_SW::DrawTriangle<SHADING, TEXTURE, RAW_TEXTURE, TRANSPARENCY, DITHERING>
|
||||
|
||||
static constexpr DrawTriangleFunction funcs[2][2][2][2][2] = {
|
||||
{{{{F(false, false, false, false, false), F(false, false, false, false, true)},
|
||||
{F(false, false, false, true, false), F(false, false, false, true, true)}},
|
||||
{{F(false, false, true, false, false), F(false, false, true, false, true)},
|
||||
{F(false, false, true, true, false), F(false, false, true, true, true)}}},
|
||||
{{{F(false, true, false, false, false), F(false, true, false, false, true)},
|
||||
{F(false, true, false, true, false), F(false, true, false, true, true)}},
|
||||
{{F(false, true, true, false, false), F(false, true, true, false, true)},
|
||||
{F(false, true, true, true, false), F(false, true, true, true, true)}}}},
|
||||
{{{{F(true, false, false, false, false), F(true, false, false, false, true)},
|
||||
{F(true, false, false, true, false), F(true, false, false, true, true)}},
|
||||
{{F(true, false, true, false, false), F(true, false, true, false, true)},
|
||||
{F(true, false, true, true, false), F(true, false, true, true, true)}}},
|
||||
{{{F(true, true, false, false, false), F(true, true, false, false, true)},
|
||||
{F(true, true, false, true, false), F(true, true, false, true, true)}},
|
||||
{{F(true, true, true, false, false), F(true, true, true, false, true)},
|
||||
{F(true, true, true, true, false), F(true, true, true, true, true)}}}}};
|
||||
|
||||
#undef F
|
||||
|
||||
return funcs[u8(shading_enable)][u8(texture_enable)][u8(raw_texture_enable)][u8(transparency_enable)]
|
||||
[u8(dithering_enable)];
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
Line_XY_FractBits = 32
|
||||
};
|
||||
enum
|
||||
{
|
||||
Line_RGB_FractBits = 12
|
||||
};
|
||||
|
||||
struct line_fxp_coord
|
||||
{
|
||||
u64 x, y;
|
||||
u32 r, g, b;
|
||||
};
|
||||
|
||||
struct line_fxp_step
|
||||
{
|
||||
s64 dx_dk, dy_dk;
|
||||
s32 dr_dk, dg_dk, db_dk;
|
||||
};
|
||||
|
||||
static ALWAYS_INLINE_RELEASE s64 LineDivide(s64 delta, s32 dk)
|
||||
{
|
||||
delta = (u64)delta << Line_XY_FractBits;
|
||||
|
||||
if (delta < 0)
|
||||
delta -= dk - 1;
|
||||
if (delta > 0)
|
||||
delta += dk - 1;
|
||||
|
||||
return (delta / dk);
|
||||
}
|
||||
|
||||
template<bool shading_enable, bool transparency_enable, bool dithering_enable>
|
||||
void GPU_SW::DrawLine(const GPUBackendDrawLineCommand* cmd, const GPUBackendDrawLineCommand::Vertex* p0,
|
||||
const GPUBackendDrawLineCommand::Vertex* p1)
|
||||
{
|
||||
// Algorithm based on Mednafen.
|
||||
if (p0->x > p1->x)
|
||||
const s32 i_dx = std::abs(p1->x - p0->x);
|
||||
const s32 i_dy = std::abs(p1->y - p0->y);
|
||||
const s32 k = (i_dx > i_dy) ? i_dx : i_dy;
|
||||
if (i_dx >= MAX_PRIMITIVE_WIDTH || i_dy >= MAX_PRIMITIVE_HEIGHT)
|
||||
return;
|
||||
|
||||
if (p0->x >= p1->x && k > 0)
|
||||
std::swap(p0, p1);
|
||||
|
||||
const s32 dx = p1->x - p0->x;
|
||||
const s32 dy = p1->y - p0->y;
|
||||
const s32 k = std::max(std::abs(dx), std::abs(dy));
|
||||
|
||||
FixedPointCoord step_x, step_y;
|
||||
FixedPointColor step_r, step_g, step_b;
|
||||
if (k > 0)
|
||||
line_fxp_step step;
|
||||
if (k == 0)
|
||||
{
|
||||
step_x = GetLineCoordStep(dx, k);
|
||||
step_y = GetLineCoordStep(dy, k);
|
||||
step.dx_dk = 0;
|
||||
step.dy_dk = 0;
|
||||
|
||||
if constexpr (shading_enable)
|
||||
{
|
||||
step_r = GetLineColorStep(s32(ZeroExtend32(p1->GetR())) - s32(ZeroExtend32(p0->GetR())), k);
|
||||
step_g = GetLineColorStep(s32(ZeroExtend32(p1->GetG())) - s32(ZeroExtend32(p0->GetG())), k);
|
||||
step_b = GetLineColorStep(s32(ZeroExtend32(p1->GetB())) - s32(ZeroExtend32(p0->GetB())), k);
|
||||
}
|
||||
else
|
||||
{
|
||||
step_r = 0;
|
||||
step_g = 0;
|
||||
step_b = 0;
|
||||
step.dr_dk = 0;
|
||||
step.dg_dk = 0;
|
||||
step.db_dk = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
step_x = 0;
|
||||
step_y = 0;
|
||||
step_r = 0;
|
||||
step_g = 0;
|
||||
step_b = 0;
|
||||
step.dx_dk = LineDivide(p1->x - p0->x, k);
|
||||
step.dy_dk = LineDivide(p1->y - p0->y, k);
|
||||
|
||||
if constexpr (shading_enable)
|
||||
{
|
||||
step.dr_dk = (s32)((u32)(p1->r - p0->r) << Line_RGB_FractBits) / k;
|
||||
step.dg_dk = (s32)((u32)(p1->g - p0->g) << Line_RGB_FractBits) / k;
|
||||
step.db_dk = (s32)((u32)(p1->b - p0->b) << Line_RGB_FractBits) / k;
|
||||
}
|
||||
}
|
||||
|
||||
FixedPointCoord current_x = IntToFixedCoord(p0->x);
|
||||
FixedPointCoord current_y = IntToFixedCoord(p0->y);
|
||||
FixedPointColor current_r = IntToFixedColor(p0->GetR());
|
||||
FixedPointColor current_g = IntToFixedColor(p0->GetG());
|
||||
FixedPointColor current_b = IntToFixedColor(p0->GetB());
|
||||
line_fxp_coord cur_point;
|
||||
cur_point.x = ((u64)p0->x << Line_XY_FractBits) | (1ULL << (Line_XY_FractBits - 1));
|
||||
cur_point.y = ((u64)p0->y << Line_XY_FractBits) | (1ULL << (Line_XY_FractBits - 1));
|
||||
|
||||
cur_point.x -= 1024;
|
||||
|
||||
if (step.dy_dk < 0)
|
||||
cur_point.y -= 1024;
|
||||
|
||||
if constexpr (shading_enable)
|
||||
{
|
||||
cur_point.r = (p0->r << Line_RGB_FractBits) | (1 << (Line_RGB_FractBits - 1));
|
||||
cur_point.g = (p0->g << Line_RGB_FractBits) | (1 << (Line_RGB_FractBits - 1));
|
||||
cur_point.b = (p0->b << Line_RGB_FractBits) | (1 << (Line_RGB_FractBits - 1));
|
||||
}
|
||||
|
||||
for (s32 i = 0; i <= k; i++)
|
||||
{
|
||||
// FIXME: Draw offset should be applied here
|
||||
const s32 x = /*m_drawing_offset.x + */ FixedToIntCoord(current_x);
|
||||
const s32 y = /*m_drawing_offset.y + */ FixedToIntCoord(current_y);
|
||||
// Sign extension is not necessary here for x and y, due to the maximum values that ClipX1 and ClipY1 can contain.
|
||||
const s32 x = (cur_point.x >> Line_XY_FractBits) & 2047;
|
||||
const s32 y = (cur_point.y >> Line_XY_FractBits) & 2047;
|
||||
|
||||
const u8 r = shading_enable ? FixedColorToInt(current_r) : p0->GetR();
|
||||
const u8 g = shading_enable ? FixedColorToInt(current_g) : p0->GetG();
|
||||
const u8 b = shading_enable ? FixedColorToInt(current_b) : p0->GetB();
|
||||
|
||||
if (x >= static_cast<s32>(m_drawing_area.left) && x <= static_cast<s32>(m_drawing_area.right) &&
|
||||
if ((!cmd->params.interlaced_rendering || cmd->params.active_line_lsb != (Truncate8(static_cast<u32>(y)) & 1u)) &&
|
||||
x >= static_cast<s32>(m_drawing_area.left) && x <= static_cast<s32>(m_drawing_area.right) &&
|
||||
y >= static_cast<s32>(m_drawing_area.top) && y <= static_cast<s32>(m_drawing_area.bottom))
|
||||
{
|
||||
const u8 r = shading_enable ? static_cast<u8>(cur_point.r >> Line_RGB_FractBits) : p0->r;
|
||||
const u8 g = shading_enable ? static_cast<u8>(cur_point.g >> Line_RGB_FractBits) : p0->g;
|
||||
const u8 b = shading_enable ? static_cast<u8>(cur_point.b >> Line_RGB_FractBits) : p0->b;
|
||||
|
||||
ShadePixel<false, false, transparency_enable, dithering_enable>(cmd, static_cast<u32>(x), static_cast<u32>(y), r,
|
||||
g, b, 0, 0);
|
||||
}
|
||||
|
||||
current_x += step_x;
|
||||
current_y += step_y;
|
||||
cur_point.x += step.dx_dk;
|
||||
cur_point.y += step.dy_dk;
|
||||
|
||||
if constexpr (shading_enable)
|
||||
{
|
||||
current_r += step_r;
|
||||
current_g += step_g;
|
||||
current_b += step_b;
|
||||
cur_point.r += step.dr_dk;
|
||||
cur_point.g += step.dg_dk;
|
||||
cur_point.b += step.db_dk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,24 +110,10 @@ protected:
|
|||
void DrawRectangle(const GPUBackendDrawRectangleCommand* cmd) override;
|
||||
void FlushRender() override;
|
||||
|
||||
static bool IsClockwiseWinding(const GPUBackendDrawPolygonCommand::Vertex* v0, const GPUBackendDrawPolygonCommand::Vertex* v1,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v2);
|
||||
|
||||
template<bool texture_enable, bool raw_texture_enable, bool transparency_enable, bool dithering_enable>
|
||||
void ShadePixel(const GPUBackendDrawCommand* cmd, u32 x, u32 y, u8 color_r, u8 color_g, u8 color_b, u8 texcoord_x,
|
||||
u8 texcoord_y);
|
||||
|
||||
template<bool shading_enable, bool texture_enable, bool raw_texture_enable, bool transparency_enable,
|
||||
bool dithering_enable>
|
||||
void DrawTriangle(const GPUBackendDrawPolygonCommand* cmd, const GPUBackendDrawPolygonCommand::Vertex* v0,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v1, const GPUBackendDrawPolygonCommand::Vertex* v2);
|
||||
|
||||
using DrawTriangleFunction = void (GPU_SW::*)(const GPUBackendDrawPolygonCommand* cmd, const GPUBackendDrawPolygonCommand::Vertex* v0,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v1,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v2);
|
||||
DrawTriangleFunction GetDrawTriangleFunction(bool shading_enable, bool texture_enable, bool raw_texture_enable,
|
||||
bool transparency_enable, bool dithering_enable);
|
||||
|
||||
template<bool texture_enable, bool raw_texture_enable, bool transparency_enable>
|
||||
void DrawRectangle(const GPUBackendDrawRectangleCommand* cmd);
|
||||
|
||||
|
@ -135,10 +121,56 @@ protected:
|
|||
DrawRectangleFunction GetDrawRectangleFunction(bool texture_enable, bool raw_texture_enable,
|
||||
bool transparency_enable);
|
||||
|
||||
template<bool shading_enable, bool transparency_enable, bool dithering_enable>
|
||||
void DrawLine(const GPUBackendDrawLineCommand* cmd, const GPUBackendDrawLineCommand::Vertex* p0, const GPUBackendDrawLineCommand::Vertex* p1);
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Polygon and line rasterization ported from Mednafen
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
struct i_deltas
|
||||
{
|
||||
u32 du_dx, dv_dx;
|
||||
u32 dr_dx, dg_dx, db_dx;
|
||||
|
||||
using DrawLineFunction = void (GPU_SW::*)(const GPUBackendDrawLineCommand* cmd, const GPUBackendDrawLineCommand::Vertex* p0,
|
||||
u32 du_dy, dv_dy;
|
||||
u32 dr_dy, dg_dy, db_dy;
|
||||
};
|
||||
|
||||
struct i_group
|
||||
{
|
||||
u32 u, v;
|
||||
u32 r, g, b;
|
||||
};
|
||||
|
||||
template<bool shading_enable, bool texture_enable>
|
||||
bool CalcIDeltas(i_deltas& idl, const GPUBackendDrawPolygonCommand::Vertex* A,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* B, const GPUBackendDrawPolygonCommand::Vertex* C);
|
||||
|
||||
template<bool shading_enable, bool texture_enable>
|
||||
void AddIDeltas_DX(i_group& ig, const i_deltas& idl, u32 count = 1);
|
||||
|
||||
template<bool shading_enable, bool texture_enable>
|
||||
void AddIDeltas_DY(i_group& ig, const i_deltas& idl, u32 count = 1);
|
||||
|
||||
template<bool shading_enable, bool texture_enable, bool raw_texture_enable, bool transparency_enable,
|
||||
bool dithering_enable>
|
||||
void DrawSpan(const GPUBackendDrawPolygonCommand* cmd, s32 y, s32 x_start, s32 x_bound, i_group ig, const i_deltas& idl);
|
||||
|
||||
template<bool shading_enable, bool texture_enable, bool raw_texture_enable, bool transparency_enable,
|
||||
bool dithering_enable>
|
||||
void DrawTriangle(const GPUBackendDrawPolygonCommand* cmd, const GPUBackendDrawPolygonCommand::Vertex* v0,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v1, const GPUBackendDrawPolygonCommand::Vertex* v2);
|
||||
|
||||
using DrawTriangleFunction = void (GPU_SW::*)(const GPUBackendDrawPolygonCommand* cmd,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v0,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v1,
|
||||
const GPUBackendDrawPolygonCommand::Vertex* v2);
|
||||
DrawTriangleFunction GetDrawTriangleFunction(bool shading_enable, bool texture_enable, bool raw_texture_enable,
|
||||
bool transparency_enable, bool dithering_enable);
|
||||
|
||||
template<bool shading_enable, bool transparency_enable, bool dithering_enable>
|
||||
void DrawLine(const GPUBackendDrawLineCommand* cmd, const GPUBackendDrawLineCommand::Vertex* p0,
|
||||
const GPUBackendDrawLineCommand::Vertex* p1);
|
||||
|
||||
using DrawLineFunction = void (GPU_SW::*)(const GPUBackendDrawLineCommand* cmd,
|
||||
const GPUBackendDrawLineCommand::Vertex* p0,
|
||||
const GPUBackendDrawLineCommand::Vertex* p1);
|
||||
DrawLineFunction GetDrawLineFunction(bool shading_enable, bool transparency_enable, bool dithering_enable);
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@ union GPUVertexPosition
|
|||
// Sprites/rectangles should be clipped to 12 bits before drawing.
|
||||
static constexpr s32 TruncateGPUVertexPosition(s32 x)
|
||||
{
|
||||
return SignExtendN<11, s32>(x);
|
||||
return SignExtendN<12, s32>(x);
|
||||
}
|
||||
|
||||
// bits in GP0(E1h) or texpage part of polygon
|
||||
|
@ -209,16 +209,12 @@ union GPUTexturePaletteReg
|
|||
ALWAYS_INLINE u32 GetYBase() const { return static_cast<u32>(y); }
|
||||
};
|
||||
|
||||
union GPUTextureWindowReg
|
||||
struct GPUTextureWindow
|
||||
{
|
||||
static constexpr u32 MASK = 0b11111111111111111111;
|
||||
|
||||
u32 bits;
|
||||
|
||||
BitField<u32, u8, 0, 5> mask_x;
|
||||
BitField<u32, u8, 5, 5> mask_y;
|
||||
BitField<u32, u8, 10, 5> offset_x;
|
||||
BitField<u32, u8, 15, 5> offset_y;
|
||||
u8 and_x;
|
||||
u8 and_y;
|
||||
u8 or_x;
|
||||
u8 or_y;
|
||||
};
|
||||
|
||||
// 4x4 dither matrix.
|
||||
|
@ -362,7 +358,7 @@ struct GPUBackendDrawCommand : public GPUBackendCommand
|
|||
GPURenderCommand rc;
|
||||
GPUDrawModeReg draw_mode;
|
||||
GPUTexturePaletteReg palette;
|
||||
GPUTextureWindowReg window;
|
||||
GPUTextureWindow window;
|
||||
Common::Rectangle<u16> bounds;
|
||||
|
||||
ALWAYS_INLINE bool IsDitheringEnabled() const { return rc.IsDitheringEnabled() && draw_mode.dither_enable; }
|
||||
|
@ -376,14 +372,22 @@ struct GPUBackendDrawPolygonCommand : public GPUBackendDrawCommand
|
|||
{
|
||||
float precise_x, precise_y, precise_w;
|
||||
s32 x, y;
|
||||
u32 color;
|
||||
u16 texcoord;
|
||||
|
||||
ALWAYS_INLINE u8 GetR() const { return Truncate8(color); }
|
||||
ALWAYS_INLINE u8 GetG() const { return Truncate8(color >> 8); }
|
||||
ALWAYS_INLINE u8 GetB() const { return Truncate8(color >> 16); }
|
||||
ALWAYS_INLINE u8 GetU() const { return Truncate8(texcoord); }
|
||||
ALWAYS_INLINE u8 GetV() const { return Truncate8(texcoord >> 8); }
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8 r, g, b, a;
|
||||
};
|
||||
u32 color;
|
||||
};
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8 u, v;
|
||||
};
|
||||
u16 texcoord;
|
||||
};
|
||||
};
|
||||
|
||||
Vertex vertices[0];
|
||||
|
@ -408,11 +412,14 @@ struct GPUBackendDrawLineCommand : public GPUBackendDrawCommand
|
|||
struct Vertex
|
||||
{
|
||||
s32 x, y;
|
||||
u32 color;
|
||||
|
||||
ALWAYS_INLINE u8 GetR() const { return Truncate8(color); }
|
||||
ALWAYS_INLINE u8 GetG() const { return Truncate8(color >> 8); }
|
||||
ALWAYS_INLINE u8 GetB() const { return Truncate8(color >> 16); }
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
u8 r, g, b, a;
|
||||
};
|
||||
u32 color;
|
||||
};
|
||||
};
|
||||
|
||||
Vertex vertices[0];
|
||||
|
|
Loading…
Reference in New Issue