mirror of
https://github.com/PCSX2/pcsx2.git
synced 2026-07-18 21:15:42 +02:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
846784650d | ||
|
|
abb32d8d84 | ||
|
|
e5e1153719 | ||
|
|
86d76bbf59 |
@@ -15134,6 +15134,14 @@ SLES-50276:
|
||||
name: "The Gift"
|
||||
name-sort: "Gift, The"
|
||||
region: "PAL-E"
|
||||
patches:
|
||||
CC76CD02:
|
||||
content: |-
|
||||
// Sound RPC races because of shared send/receive buffers with badly
|
||||
// placed wait loop, fix by forcing all sound rpc to be synchronous.
|
||||
// Ideal fix would be moving the rpc wait loop though.
|
||||
author=Ziemas
|
||||
patch=0,EE,002159e0,word,00000000
|
||||
SLES-50277:
|
||||
name: "Red Faction"
|
||||
region: "PAL-E"
|
||||
|
||||
+238
-264
@@ -289,55 +289,58 @@ void GSState::ResetDrawBufferIdx()
|
||||
{
|
||||
int entry_ptr = 0;
|
||||
|
||||
for (int i = 0; i < m_used_buffers_idx; i++)
|
||||
if (GSConfig.UserHacks_DrawBuffering && m_used_buffers_idx > 1)
|
||||
{
|
||||
// There can be situations like VSync where it won't purge the draws, this is bad for us!
|
||||
if (m_index_buffers[i].tail > 0 || i == m_current_buffer_idx)
|
||||
for (int i = 0; i < m_used_buffers_idx; i++)
|
||||
{
|
||||
if (m_index_buffers[i].tail == 0)
|
||||
m_env_buffers[i].draw_rect = GSVector4i::zero();
|
||||
|
||||
if (entry_ptr == i && (m_index_buffers[i].tail > 0 || i == m_current_buffer_idx))
|
||||
// There can be situations like VSync where it won't purge the draws, this is bad for us!
|
||||
if (m_index_buffers[i].tail > 0 || i == m_current_buffer_idx)
|
||||
{
|
||||
if (m_index_buffers[i].tail == 0)
|
||||
m_env_buffers[i].draw_rect = GSVector4i::zero();
|
||||
|
||||
if (entry_ptr == i && (m_index_buffers[i].tail > 0 || i == m_current_buffer_idx))
|
||||
{
|
||||
entry_ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(m_vertex_buffers[entry_ptr].buff, m_vertex_buffers[i].buff, sizeof(GSVertex) * m_vertex_buffers[i].tail);
|
||||
|
||||
m_vertex_buffers[entry_ptr].head = m_vertex_buffers[i].head;
|
||||
m_vertex_buffers[entry_ptr].tail = m_vertex_buffers[i].tail;
|
||||
m_vertex_buffers[entry_ptr].next = m_vertex_buffers[i].next;
|
||||
|
||||
memcpy(m_index_buffers[entry_ptr].buff, m_index_buffers[i].buff, sizeof(u16) * m_index_buffers[i].tail);
|
||||
m_index_buffers[entry_ptr].tail = m_index_buffers[i].tail;
|
||||
|
||||
if (m_vertex_buffers[entry_ptr].tail != 0)
|
||||
{
|
||||
memcpy(m_vertex_buffers[entry_ptr].xy, m_vertex_buffers[i].xy, sizeof(m_vertex_buffers[i].xy));
|
||||
m_vertex_buffers[entry_ptr].xyhead = m_vertex_buffers[i].xyhead;
|
||||
m_vertex_buffers[entry_ptr].xy_tail = m_vertex_buffers[i].xy_tail;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vertex_buffers[entry_ptr].xy_tail = 0;
|
||||
}
|
||||
|
||||
memcpy(&m_env_buffers[entry_ptr], &m_env_buffers[i], sizeof(m_env_buffers[i]));
|
||||
|
||||
|
||||
if (i == m_current_buffer_idx)
|
||||
m_current_buffer_idx = entry_ptr;
|
||||
|
||||
entry_ptr++;
|
||||
continue;
|
||||
}
|
||||
|
||||
memcpy(m_vertex_buffers[entry_ptr].buff, m_vertex_buffers[i].buff, sizeof(GSVertex) * m_vertex_buffers[i].tail);
|
||||
|
||||
m_vertex_buffers[entry_ptr].head = m_vertex_buffers[i].head;
|
||||
m_vertex_buffers[entry_ptr].tail = m_vertex_buffers[i].tail;
|
||||
m_vertex_buffers[entry_ptr].next = m_vertex_buffers[i].next;
|
||||
|
||||
memcpy(m_index_buffers[entry_ptr].buff, m_index_buffers[i].buff, sizeof(u16) * m_index_buffers[i].tail);
|
||||
m_index_buffers[entry_ptr].tail = m_index_buffers[i].tail;
|
||||
|
||||
if (m_vertex_buffers[entry_ptr].tail != 0)
|
||||
if (i != (entry_ptr - 1))
|
||||
{
|
||||
memcpy(m_vertex_buffers[entry_ptr].xy, m_vertex_buffers[i].xy, sizeof(m_vertex_buffers[i].xy));
|
||||
m_vertex_buffers[entry_ptr].xyhead = m_vertex_buffers[i].xyhead;
|
||||
m_vertex_buffers[entry_ptr].xy_tail = m_vertex_buffers[i].xy_tail;
|
||||
m_index_buffers[i].tail = 0;
|
||||
memset(&m_env_buffers[i], 0, sizeof(GSDrawBufferEnv));
|
||||
m_vertex_buffers[i].head = m_vertex_buffers[i].tail = m_vertex_buffers[i].next = 0;
|
||||
m_vertex_buffers[i].xy_tail = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vertex_buffers[entry_ptr].xy_tail = 0;
|
||||
}
|
||||
|
||||
memcpy(&m_env_buffers[entry_ptr], &m_env_buffers[i], sizeof(m_env_buffers[i]));
|
||||
|
||||
|
||||
if (i == m_current_buffer_idx)
|
||||
m_current_buffer_idx = entry_ptr;
|
||||
|
||||
entry_ptr++;
|
||||
}
|
||||
|
||||
if (i != (entry_ptr - 1))
|
||||
{
|
||||
m_index_buffers[i].tail = 0;
|
||||
memset(&m_env_buffers[i], 0, sizeof(GSDrawBufferEnv));
|
||||
m_vertex_buffers[i].head = m_vertex_buffers[i].tail = m_vertex_buffers[i].next = 0;
|
||||
m_vertex_buffers[i].xy_tail = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -389,18 +392,11 @@ void GSState::ResetDrawBuffers()
|
||||
// exclude_current is used if there is a flush for a reason other than the normal context change.
|
||||
void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushReason flush_reason)
|
||||
{
|
||||
const int current_idx = m_current_buffer_idx;
|
||||
bool restore_env = false;
|
||||
|
||||
if (m_used_buffers_idx > 0)
|
||||
if (m_used_buffers_idx > 1)
|
||||
{
|
||||
if (m_used_buffers_idx > 1)
|
||||
{
|
||||
restore_env = true;
|
||||
memcpy(&m_temp_env, &m_env, sizeof(m_env));
|
||||
}
|
||||
else if (m_index_buffers[0].tail == 0)
|
||||
return;
|
||||
const int current_idx = m_current_buffer_idx;
|
||||
bool restore_env = true;
|
||||
memcpy(&m_temp_env, &m_env, sizeof(m_env));
|
||||
|
||||
const int max_flushes = flush_base_only ? 1 : m_used_buffers_idx;
|
||||
for (int i = 0; i < max_flushes; i++)
|
||||
@@ -440,19 +436,26 @@ void GSState::FlushBuffers(bool flush_base_only, bool use_flush_reason, GSFlushR
|
||||
else
|
||||
FlushDraw(GSFlushReason::CONTEXTCHANGE);
|
||||
}
|
||||
|
||||
// Restore the environment
|
||||
m_current_buffer_idx = current_idx;
|
||||
m_index = &m_index_buffers[m_current_buffer_idx];
|
||||
m_vertex = &m_vertex_buffers[m_current_buffer_idx];
|
||||
|
||||
const int ctx = m_env_buffers[m_current_buffer_idx].m_backed_up_ctx;
|
||||
std::memcpy(&m_prev_env, &m_env_buffers[m_current_buffer_idx].m_env, 88);
|
||||
std::memcpy(&m_prev_env.CTXT[0], &m_env_buffers[m_current_buffer_idx].m_env.CTXT[0], 96);
|
||||
std::memcpy(&m_prev_env.CTXT[1], &m_env_buffers[m_current_buffer_idx].m_env.CTXT[1], 96);
|
||||
std::memcpy(&m_prev_env.CTXT[ctx].offset, &m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].offset, sizeof(m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].offset));
|
||||
std::memcpy(&m_prev_env.CTXT[ctx].scissor, &m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].scissor, sizeof(m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].scissor));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (use_flush_reason || flush_reason == VSYNC)
|
||||
FlushDraw(flush_reason);
|
||||
else
|
||||
FlushDraw(GSFlushReason::CONTEXTCHANGE);
|
||||
}
|
||||
|
||||
// Restore the environment
|
||||
m_current_buffer_idx = current_idx;
|
||||
m_index = &m_index_buffers[m_current_buffer_idx];
|
||||
m_vertex = &m_vertex_buffers[m_current_buffer_idx];
|
||||
|
||||
const int ctx = m_env_buffers[m_current_buffer_idx].m_backed_up_ctx;
|
||||
std::memcpy(&m_prev_env, &m_env_buffers[m_current_buffer_idx].m_env, 88);
|
||||
std::memcpy(&m_prev_env.CTXT[0], &m_env_buffers[m_current_buffer_idx].m_env.CTXT[0], 96);
|
||||
std::memcpy(&m_prev_env.CTXT[1], &m_env_buffers[m_current_buffer_idx].m_env.CTXT[1], 96);
|
||||
std::memcpy(&m_prev_env.CTXT[ctx].offset, &m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].offset, sizeof(m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].offset));
|
||||
std::memcpy(&m_prev_env.CTXT[ctx].scissor, &m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].scissor, sizeof(m_env_buffers[m_current_buffer_idx].m_env.CTXT[ctx].scissor));
|
||||
}
|
||||
|
||||
void GSState::PushBuffer()
|
||||
@@ -469,6 +472,8 @@ void GSState::PushBuffer()
|
||||
{
|
||||
m_index = &m_index_buffers[m_used_buffers_idx];
|
||||
m_vertex = &m_vertex_buffers[m_used_buffers_idx];
|
||||
GSVertexBuff& vtx_buff = *m_vertex;
|
||||
GSIndexBuff& idx_buff = *m_index;
|
||||
|
||||
const u32 base = m_vertex_buffers[m_current_buffer_idx].head;
|
||||
const u32 copy_amt = m_vertex_buffers[m_current_buffer_idx].tail - base;
|
||||
@@ -476,25 +481,25 @@ void GSState::PushBuffer()
|
||||
m_vertex->tail = 0;
|
||||
|
||||
if (copy_amt)
|
||||
memcpy(m_vertex->buff, &m_vertex_buffers[m_current_buffer_idx].buff[base], sizeof(GSVertex) * copy_amt);
|
||||
memcpy(vtx_buff.buff, &m_vertex_buffers[m_current_buffer_idx].buff[base], sizeof(GSVertex) * copy_amt);
|
||||
|
||||
m_vertex->head = 0;
|
||||
m_vertex->next = 0;
|
||||
m_vertex->tail += copy_amt;
|
||||
vtx_buff.head = 0;
|
||||
vtx_buff.next = 0;
|
||||
vtx_buff.tail += copy_amt;
|
||||
|
||||
if (copy_amt)
|
||||
{
|
||||
for (u32 i = 0; i < copy_amt; i++)
|
||||
{
|
||||
m_vertex->xy[i & 3] = m_vertex_buffers[m_current_buffer_idx].xy[((m_vertex_buffers[m_current_buffer_idx].xy_tail - copy_amt) + i) & 3];
|
||||
m_vertex->xy_tail++;
|
||||
vtx_buff.xy[i & 3] = m_vertex_buffers[m_current_buffer_idx].xy[((m_vertex_buffers[m_current_buffer_idx].xy_tail - copy_amt) + i) & 3];
|
||||
vtx_buff.xy_tail++;
|
||||
|
||||
if (i == 0)
|
||||
m_vertex->xyhead = m_vertex_buffers[m_current_buffer_idx].xyhead;
|
||||
vtx_buff.xyhead = m_vertex_buffers[m_current_buffer_idx].xyhead;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_vertex->xy_tail = 0;
|
||||
vtx_buff.xy_tail = 0;
|
||||
|
||||
m_current_buffer_idx = m_used_buffers_idx;
|
||||
temp_draw_rect = GSVector4i::zero();
|
||||
@@ -504,6 +509,18 @@ void GSState::PushBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
void GSState::SetDrawBufferEnv()
|
||||
{
|
||||
memcpy(&m_env_buffers[m_current_buffer_idx].m_env, &m_env, sizeof(GSDrawingEnvironment));
|
||||
m_env_buffers[m_current_buffer_idx].m_backed_up_ctx = m_backed_up_ctx;
|
||||
}
|
||||
|
||||
void GSState::SetDrawBuffDirty()
|
||||
{
|
||||
m_env_buffers[m_current_buffer_idx].m_dirty_regs = m_dirty_gs_regs;
|
||||
m_env_buffers[m_current_buffer_idx].draw_rect = temp_draw_rect;
|
||||
}
|
||||
|
||||
bool GSState::CanBufferNewDraw()
|
||||
{
|
||||
if (!GSConfig.UserHacks_DrawBuffering)
|
||||
@@ -618,21 +635,23 @@ bool GSState::CanBufferNewDraw()
|
||||
// We found a matching draw
|
||||
m_index = &m_index_buffers[i];
|
||||
m_vertex = &m_vertex_buffers[i];
|
||||
GSVertexBuff& vtx_buff = *m_vertex;
|
||||
GSIndexBuff& idx_buff = *m_index;
|
||||
|
||||
const u32 copy_amt = m_vertex_buffers[m_current_buffer_idx].tail - m_vertex_buffers[m_current_buffer_idx].head;
|
||||
|
||||
m_recent_buffer_switch = m_vertex->tail == m_vertex->head;
|
||||
if (m_index->tail)
|
||||
m_vertex->tail = m_index->buff[m_index->tail - 1] + 1;
|
||||
m_recent_buffer_switch = vtx_buff.tail == vtx_buff.head;
|
||||
if (idx_buff.tail)
|
||||
vtx_buff.tail = idx_buff.buff[idx_buff.tail - 1] + 1;
|
||||
else
|
||||
m_vertex->tail = 0;
|
||||
vtx_buff.tail = 0;
|
||||
|
||||
if (copy_amt)
|
||||
memcpy(&m_vertex->buff[m_vertex->tail], &m_vertex_buffers[m_current_buffer_idx].buff[m_vertex_buffers[m_current_buffer_idx].head], sizeof(GSVertex) * copy_amt);
|
||||
memcpy(&vtx_buff.buff[vtx_buff.tail], &m_vertex_buffers[m_current_buffer_idx].buff[m_vertex_buffers[m_current_buffer_idx].head], sizeof(GSVertex) * copy_amt);
|
||||
|
||||
m_vertex->head = m_vertex->tail;
|
||||
m_vertex->next = m_vertex->head;
|
||||
m_vertex->tail += copy_amt;
|
||||
vtx_buff.head = vtx_buff.tail;
|
||||
vtx_buff.next = vtx_buff.head;
|
||||
vtx_buff.tail += copy_amt;
|
||||
m_backed_up_ctx = m_env_buffers[i].m_backed_up_ctx;
|
||||
temp_draw_rect = m_env_buffers[i].draw_rect;
|
||||
m_env_buffers[i].m_dirty_regs = 0;
|
||||
@@ -648,15 +667,15 @@ bool GSState::CanBufferNewDraw()
|
||||
{
|
||||
for (u32 i = 0; i < copy_amt; i++)
|
||||
{
|
||||
m_vertex->xy[m_vertex->xy_tail & 3] = m_vertex_buffers[m_current_buffer_idx].xy[((m_vertex_buffers[m_current_buffer_idx].xy_tail - copy_amt) + i) & 3];
|
||||
m_vertex->xy_tail++;
|
||||
vtx_buff.xy[vtx_buff.xy_tail & 3] = m_vertex_buffers[m_current_buffer_idx].xy[((m_vertex_buffers[m_current_buffer_idx].xy_tail - copy_amt) + i) & 3];
|
||||
vtx_buff.xy_tail++;
|
||||
|
||||
if (i == 0)
|
||||
m_vertex->xyhead = m_vertex_buffers[m_current_buffer_idx].xyhead;
|
||||
vtx_buff.xyhead = m_vertex_buffers[m_current_buffer_idx].xyhead;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_vertex->xy_tail = 0;
|
||||
vtx_buff.xy_tail = 0;
|
||||
|
||||
m_current_buffer_idx = i;
|
||||
|
||||
@@ -692,18 +711,6 @@ bool GSState::CanBufferNewDraw()
|
||||
return true;
|
||||
}
|
||||
|
||||
void GSState::SetDrawBufferEnv()
|
||||
{
|
||||
memcpy(&m_env_buffers[m_current_buffer_idx].m_env, &m_env, sizeof(GSDrawingEnvironment));
|
||||
m_env_buffers[m_current_buffer_idx].m_backed_up_ctx = m_backed_up_ctx;
|
||||
}
|
||||
|
||||
void GSState::SetDrawBuffDirty()
|
||||
{
|
||||
m_env_buffers[m_current_buffer_idx].m_dirty_regs = m_dirty_gs_regs;
|
||||
m_env_buffers[m_current_buffer_idx].draw_rect = temp_draw_rect;
|
||||
}
|
||||
|
||||
void GSState::ResetHandlers()
|
||||
{
|
||||
std::fill(std::begin(m_fpGIFPackedRegHandlers), std::end(m_fpGIFPackedRegHandlers), &GSState::GIFPackedRegHandlerNull);
|
||||
@@ -1673,7 +1680,7 @@ void GSState::ApplyTEX0(GIFRegTEX0& TEX0)
|
||||
{
|
||||
for (int b = 0; b < m_used_buffers_idx; b++)
|
||||
{
|
||||
GSDrawingEnvironment& buffered_env = m_env_buffers[b].m_env;
|
||||
GSDrawingEnvironment& buffered_env = (m_current_buffer_idx == b) ? m_prev_env : m_env_buffers[b].m_env;
|
||||
if ((buffered_env.PRIM.TME && (buffered_env.CTXT[buffered_env.PRIM.CTXT].TEX0.PSM & 0x7) >= 3) || (m_mem.m_clut.IsInvalid() & 2))
|
||||
Flush(GSFlushReason::CLUTCHANGE);
|
||||
}
|
||||
@@ -2509,11 +2516,13 @@ void GSState::FlushPrim()
|
||||
}
|
||||
|
||||
GSVertex buff[2];
|
||||
GSVertexBuff& vtx_buff = *m_vertex;
|
||||
GSIndexBuff& idx_buff = *m_index;
|
||||
s_n++;
|
||||
|
||||
const u32 head = m_vertex->head;
|
||||
const u32 tail = m_vertex->tail;
|
||||
const u32 next = m_vertex->next;
|
||||
const u32 head = vtx_buff.head;
|
||||
const u32 tail = vtx_buff.tail;
|
||||
const u32 next = vtx_buff.next;
|
||||
u32 unused = 0;
|
||||
|
||||
if (tail > head)
|
||||
@@ -2527,19 +2536,19 @@ void GSState::FlushPrim()
|
||||
case GS_LINESTRIP:
|
||||
case GS_SPRITE:
|
||||
unused = 1;
|
||||
buff[0] = m_vertex->buff[tail - 1];
|
||||
buff[0] = vtx_buff.buff[tail - 1];
|
||||
break;
|
||||
case GS_TRIANGLELIST:
|
||||
case GS_TRIANGLESTRIP:
|
||||
unused = std::min<u32>(tail - head, 2);
|
||||
memcpy(buff, &m_vertex->buff[tail - unused], sizeof(GSVertex) * 2);
|
||||
memcpy(buff, &vtx_buff.buff[tail - unused], sizeof(GSVertex) * 2);
|
||||
break;
|
||||
case GS_TRIANGLEFAN:
|
||||
buff[0] = m_vertex->buff[head];
|
||||
buff[0] = vtx_buff.buff[head];
|
||||
unused = 1;
|
||||
if (tail - 1 > head)
|
||||
{
|
||||
buff[1] = m_vertex->buff[tail - 1];
|
||||
buff[1] = vtx_buff.buff[tail - 1];
|
||||
unused = 2;
|
||||
}
|
||||
break;
|
||||
@@ -2563,7 +2572,7 @@ void GSState::FlushPrim()
|
||||
#endif
|
||||
// Update scissor, it may have been modified by a previous draw
|
||||
m_env.CTXT[PRIM->CTXT].UpdateScissor();
|
||||
m_vt.Update(m_vertex->buff, m_index->buff, m_vertex->tail, m_index->tail, GSUtil::GetPrimClass(PRIM->PRIM));
|
||||
m_vt.Update(vtx_buff.buff, idx_buff.buff, vtx_buff.tail, idx_buff.tail, GSUtil::GetPrimClass(PRIM->PRIM));
|
||||
|
||||
// Texel coordinate rounding
|
||||
// Helps Manhunt (lights shining through objects).
|
||||
@@ -2575,13 +2584,13 @@ void GSState::FlushPrim()
|
||||
{
|
||||
const bool is_sprite = GSUtil::GetPrimClass(PRIM->PRIM) == GS_PRIM_CLASS::GS_SPRITE_CLASS;
|
||||
// ST's have the lowest 9 bits (or greater depending on exponent difference) rounding down (from hardware tests).
|
||||
for (int i = m_index->tail - 1; i >= 0; i--)
|
||||
for (int i = idx_buff.tail - 1; i >= 0; i--)
|
||||
{
|
||||
GSVertex* v = &m_vertex->buff[m_index->buff[i]];
|
||||
GSVertex* v = &vtx_buff.buff[idx_buff.buff[i]];
|
||||
|
||||
// Only Q on the second vertex is valid
|
||||
if (!(i & 1) && is_sprite)
|
||||
v->RGBAQ.Q = m_vertex->buff[m_index->buff[i + 1]].RGBAQ.Q;
|
||||
v->RGBAQ.Q = vtx_buff.buff[idx_buff.buff[i + 1]].RGBAQ.Q;
|
||||
|
||||
int T = std::bit_cast<int>(v->ST.T);
|
||||
int Q = std::bit_cast<int>(v->RGBAQ.Q);
|
||||
@@ -2634,7 +2643,7 @@ void GSState::FlushPrim()
|
||||
Draw();
|
||||
|
||||
g_perfmon.Put(GSPerfMon::Draw, 1);
|
||||
g_perfmon.Put(GSPerfMon::Prim, m_index->tail / GSUtil::GetVertexCount(PRIM->PRIM));
|
||||
g_perfmon.Put(GSPerfMon::Prim, idx_buff.tail / GSUtil::GetVertexCount(PRIM->PRIM));
|
||||
|
||||
if (GSConfig.ShouldDump(s_n, g_perfmon.GetFrame()))
|
||||
{
|
||||
@@ -2646,15 +2655,15 @@ void GSState::FlushPrim()
|
||||
}
|
||||
}
|
||||
|
||||
m_index->tail = 0;
|
||||
m_vertex->head = 0;
|
||||
idx_buff.tail = 0;
|
||||
vtx_buff.head = 0;
|
||||
|
||||
if (unused > 0)
|
||||
{
|
||||
memcpy(m_vertex->buff, buff, sizeof(GSVertex) * unused);
|
||||
memcpy(vtx_buff.buff, buff, sizeof(GSVertex) * unused);
|
||||
|
||||
m_vertex->tail = unused;
|
||||
m_vertex->next = next > head ? next - head : 0;
|
||||
vtx_buff.tail = unused;
|
||||
vtx_buff.next = next > head ? next - head : 0;
|
||||
|
||||
// If it's a Triangle fan the XY buffer needs to be updated to point to the correct head vert
|
||||
// Jak 3 shadows get spikey (with autoflush) if you don't.
|
||||
@@ -2662,22 +2671,22 @@ void GSState::FlushPrim()
|
||||
{
|
||||
for (u32 i = 0; i < unused; i++)
|
||||
{
|
||||
GSVector4i* RESTRICT vert_ptr = (GSVector4i*)&m_vertex->buff[i];
|
||||
GSVector4i* RESTRICT vert_ptr = (GSVector4i*)&vtx_buff.buff[i];
|
||||
GSVector4i v = vert_ptr[1];
|
||||
v = v.xxxx().u16to32().sub32(m_xyof);
|
||||
m_vertex->xy[i & 3] = v;
|
||||
m_vertex->xy_tail = unused;
|
||||
vtx_buff.xy[i & 3] = v;
|
||||
vtx_buff.xy_tail = unused;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vertex->tail = 0;
|
||||
m_vertex->next = 0;
|
||||
vtx_buff.tail = 0;
|
||||
vtx_buff.next = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
GSVector4i GSState::GetTEX0Rect(GSDrawingContext prev_ctx)
|
||||
GSVector4i GSState::GetTEX0Rect(const GSDrawingContext& prev_ctx)
|
||||
{
|
||||
GSVector4i ret = GSVector4i::zero();
|
||||
|
||||
@@ -2723,27 +2732,19 @@ void GSState::CheckWriteOverlap(bool req_write, bool req_read)
|
||||
const GIFRegBITBLTBUF& blit = m_env.BITBLTBUF;
|
||||
|
||||
const GSVector4i write_rect = GSVector4i(m_env.TRXPOS.DSAX, m_env.TRXPOS.DSAY, m_env.TRXPOS.DSAX + w, m_env.TRXPOS.DSAY + h);
|
||||
const u32 write_start_bp = GSLocalMemory::GetStartBlockAddress(blit.DBP, blit.DBW, blit.DPSM, write_rect);
|
||||
const u32 write_end_bp = ((GSLocalMemory::GetEndBlockAddress(blit.DBP, blit.DBW, blit.DPSM, write_rect) + 1) + (GS_BLOCKS_PER_PAGE - 1)) & ~(GS_BLOCKS_PER_PAGE - 1);
|
||||
|
||||
for (int i = 0; i < m_used_buffers_idx; i++)
|
||||
{
|
||||
GSIndexBuff* cur_index_buff = &m_index_buffers[i];
|
||||
GSVertexBuff* cur_vertex_buff = &m_vertex_buffers[i];
|
||||
const GSDrawingContext& prev_ctx = m_env_buffers[i].m_env.CTXT[m_env_buffers[i].m_backed_up_ctx];
|
||||
const GSDrawingEnvironment& prev_env = m_env_buffers[i].m_env;
|
||||
GSVector4i tex_rect = prev_env.PRIM.TME ? GetTEX0Rect(prev_ctx) : GSVector4i::zero();
|
||||
const GSDrawingContext& prev_ctx = m_current_buffer_idx == i ? m_prev_env.CTXT[m_backed_up_ctx] :m_env_buffers[i].m_env.CTXT[m_env_buffers[i].m_backed_up_ctx];
|
||||
const GSDrawingEnvironment& prev_env = m_current_buffer_idx == i ? m_prev_env : m_env_buffers[i].m_env;
|
||||
|
||||
if (cur_index_buff->tail > 0)
|
||||
{
|
||||
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
|
||||
// Check Fast & Furious (Hardare mode) and Assault Suits Valken (either renderer) and Tomb Raider - Angel of Darkness menu (TBP != DBP but overlaps).
|
||||
// Cartoon Network overwrites its own Z buffer in the middle of a draw.
|
||||
// Alias wraps its transfers, so be careful
|
||||
const GSVector4i read_rect = GSVector4i(m_env.TRXPOS.SSAX, m_env.TRXPOS.SSAY, m_env.TRXPOS.SSAX + w, m_env.TRXPOS.SSAY + h);
|
||||
|
||||
if (req_write && prev_env.PRIM.TME)
|
||||
{
|
||||
GSVector4i tex_rect = prev_env.PRIM.TME ? GetTEX0Rect(prev_ctx) : GSVector4i::zero();
|
||||
// Tex rect could be invalid showing 1024x1024 when it isn't. If the frame is only 1 page wide, it's either a big strip or a single page draw.
|
||||
// This large texture causes misdetection of overlapping writes, causing our heuristics in the hardware renderer for future draws to be missing.
|
||||
// Either way if we check the queued up coordinates, it should give us a fair idea. (Cabela's Trophy Bucks)
|
||||
@@ -2842,6 +2843,11 @@ void GSState::CheckWriteOverlap(bool req_write, bool req_read)
|
||||
}
|
||||
}
|
||||
|
||||
// Only flush on a NEW transfer if a pending one is using the same address or overlap.
|
||||
// Check Fast & Furious (Hardare mode) and Assault Suits Valken (either renderer) and Tomb Raider - Angel of Darkness menu (TBP != DBP but overlaps).
|
||||
// Cartoon Network overwrites its own Z buffer in the middle of a draw.
|
||||
// Alias wraps its transfers, so be careful
|
||||
const GSVector4i read_rect = GSVector4i(m_env.TRXPOS.SSAX, m_env.TRXPOS.SSAY, m_env.TRXPOS.SSAX + w, m_env.TRXPOS.SSAY + h);
|
||||
const u32 frame_mask = GSLocalMemory::m_psm[prev_ctx.FRAME.PSM].fmsk;
|
||||
const bool frame_required = (!(prev_ctx.TEST.ATE && prev_ctx.TEST.ATST == 0 && (prev_ctx.TEST.AFAIL == 2 || prev_ctx.TEST.AFAIL == 0)) && ((prev_ctx.FRAME.FBMSK & frame_mask) != frame_mask)) || prev_ctx.TEST.DATE;
|
||||
const GSVector4i draw_rect = (m_current_buffer_idx == i) ? temp_draw_rect : m_env_buffers[i].draw_rect;
|
||||
@@ -2874,6 +2880,9 @@ void GSState::CheckWriteOverlap(bool req_write, bool req_read)
|
||||
|
||||
if (req_write)
|
||||
{
|
||||
const u32 write_start_bp = GSLocalMemory::GetStartBlockAddress(blit.DBP, blit.DBW, blit.DPSM, write_rect);
|
||||
const u32 write_end_bp = ((GSLocalMemory::GetEndBlockAddress(blit.DBP, blit.DBW, blit.DPSM, write_rect) + 1) + (GS_BLOCKS_PER_PAGE - 1)) & ~(GS_BLOCKS_PER_PAGE - 1);
|
||||
|
||||
// Invalid the CLUT if it crosses paths.
|
||||
m_mem.m_clut.InvalidateRange(write_start_bp, write_end_bp);
|
||||
}
|
||||
@@ -4771,12 +4780,12 @@ bool GSState::SpriteDrawWithoutGaps()
|
||||
const GSVertex* v = &m_vertex->buff[0];
|
||||
const int first_dpY = v[1].XYZ.Y - v[0].XYZ.Y;
|
||||
const int first_dpX = v[1].XYZ.X - v[0].XYZ.X;
|
||||
|
||||
const u32 next_count = m_vertex->next;
|
||||
// Horizontal Match.
|
||||
if (((first_dpX + 8) >> 4) == m_r_no_scissor.z)
|
||||
{
|
||||
// Borrowed from MergeSprite() modified to calculate heights.
|
||||
for (u32 i = 2; i < m_vertex->next; i += 2)
|
||||
for (u32 i = 2; i < next_count; i += 2)
|
||||
{
|
||||
const int last_pY = v[i - 1].XYZ.Y;
|
||||
const int dpY = v[i + 1].XYZ.Y - v[i].XYZ.Y;
|
||||
@@ -4793,7 +4802,7 @@ bool GSState::SpriteDrawWithoutGaps()
|
||||
{
|
||||
// Borrowed from MergeSprite().
|
||||
const int offset_X = m_context->XYOFFSET.OFX;
|
||||
for (u32 i = 2; i < m_vertex->next; i += 2)
|
||||
for (u32 i = 2; i < next_count; i += 2)
|
||||
{
|
||||
const int last_pX = v[i - 1].XYZ.X;
|
||||
const int this_start_X = v[i].XYZ.X;
|
||||
@@ -4810,7 +4819,7 @@ bool GSState::SpriteDrawWithoutGaps()
|
||||
else
|
||||
{
|
||||
const int dpY = v[i + 1].XYZ.Y - v[i].XYZ.Y;
|
||||
if ((std::abs(dpY - first_dpY) >= 16 && (i + 2) < m_vertex->next) || std::abs(this_start_X - last_pX) >= 16)
|
||||
if ((std::abs(dpY - first_dpY) >= 16 && (i + 2) < next_count) || std::abs(this_start_X - last_pX) >= 16)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -4823,7 +4832,7 @@ bool GSState::SpriteDrawWithoutGaps()
|
||||
{
|
||||
int lastXEdge = std::max(v[1].XYZ.X, v[0].XYZ.X);
|
||||
int lastYEdge = std::max(v[1].XYZ.Y, v[0].XYZ.Y);
|
||||
for (u32 i = 2; i < m_vertex->next; i += 2)
|
||||
for (u32 i = 2; i < next_count; i += 2)
|
||||
{
|
||||
const int dpY = v[i + 1].XYZ.Y - v[i].XYZ.Y;
|
||||
|
||||
@@ -5405,7 +5414,7 @@ __forceinline void GSState::CheckCLUTValidity(u32 prim)
|
||||
|
||||
for (int i = 0; i < m_used_buffers_idx; i++)
|
||||
{
|
||||
GSDrawingEnvironment& buffered_env = m_env_buffers[i].m_env;
|
||||
GSDrawingEnvironment& buffered_env = (m_current_buffer_idx == i) ? m_prev_env : m_env_buffers[i].m_env;
|
||||
const GSDrawingContext& ctx = buffered_env.CTXT[buffered_env.PRIM.CTXT];
|
||||
if ((m_index_buffers[i].tail > 0 || (m_vertex_buffers[i].tail == n - 1)) && (GSLocalMemory::m_psm[ctx.TEX0.PSM].pal == 0 || !buffered_env.PRIM.TME))
|
||||
{
|
||||
@@ -5421,7 +5430,8 @@ __forceinline void GSState::CheckCLUTValidity(u32 prim)
|
||||
if (prim != GS_POINTLIST || (m_index_buffers[i].tail > 1))
|
||||
endbp = fpsm.info.bn(temp_draw_rect.z - 1, temp_draw_rect.w - 1, ctx.FRAME.Block(), ctx.FRAME.FBW);
|
||||
|
||||
m_mem.m_clut.InvalidateRange(startbp, endbp, true);
|
||||
if (m_mem.m_clut.InvalidateRange(startbp, endbp, true))
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5434,6 +5444,8 @@ __forceinline void GSState::HandleAutoFlush()
|
||||
if ((m_index->tail & 1) && (prim == GS_TRIANGLESTRIP || prim == GS_TRIANGLEFAN) && !m_texflush_flag)
|
||||
return;
|
||||
|
||||
GSVertexBuff& vtx_buff = *m_vertex;
|
||||
GSIndexBuff& idx_buff = *m_index;
|
||||
// To briefly explain what's going on here, what we are checking for is draws over a texture when the source and destination are themselves.
|
||||
// Because one page of the texture gets buffered in the Texture Cache (the PS2's one) if any of those pixels are overwritten, you still read the old data.
|
||||
// So we need to calculate if a page boundary is being crossed for the format it is in and if the same part of the texture being written and read inside the draw.
|
||||
@@ -5442,8 +5454,8 @@ __forceinline void GSState::HandleAutoFlush()
|
||||
{
|
||||
int n = 1;
|
||||
u32 buff[3];
|
||||
const u32 head = m_vertex->head;
|
||||
const u32 tail = m_vertex->tail;
|
||||
const u32 head = vtx_buff.head;
|
||||
const u32 tail = vtx_buff.tail;
|
||||
|
||||
switch (prim)
|
||||
{
|
||||
@@ -5510,7 +5522,7 @@ __forceinline void GSState::HandleAutoFlush()
|
||||
// Get the rest of the rect.
|
||||
for (int i = 0; i < (n - 1); i++)
|
||||
{
|
||||
const GSVertex* v = &m_vertex->buff[buff[i]];
|
||||
const GSVertex* v = &vtx_buff.buff[buff[i]];
|
||||
|
||||
xy_coord.x = (static_cast<int>(v->XYZ.X) - static_cast<int>(m_context->XYOFFSET.OFX)) >> 4;
|
||||
xy_coord.y = (static_cast<int>(v->XYZ.Y) - static_cast<int>(m_context->XYOFFSET.OFY)) >> 4;
|
||||
@@ -5562,7 +5574,7 @@ __forceinline void GSState::HandleAutoFlush()
|
||||
return;
|
||||
|
||||
// Get the last texture position from the last draw.
|
||||
const GSVertex* v = &m_vertex->buff[m_index->buff[m_index->tail - 1]];
|
||||
const GSVertex* v = &vtx_buff.buff[idx_buff.buff[idx_buff.tail - 1]];
|
||||
|
||||
if (PRIM->FST)
|
||||
{
|
||||
@@ -5652,13 +5664,13 @@ __forceinline void GSState::HandleAutoFlush()
|
||||
const GSVector2i offset = GSVector2i(m_context->XYOFFSET.OFX, m_context->XYOFFSET.OFY);
|
||||
const GSVector4i scissor = m_context->scissor.in;
|
||||
GSVector4i old_draw_rect = GSVector4i::zero();
|
||||
int current_draw_end = m_index->tail;
|
||||
int current_draw_end = idx_buff.tail;
|
||||
|
||||
while (current_draw_end >= n)
|
||||
{
|
||||
for (int i = current_draw_end - 1; i >= current_draw_end - n; i--)
|
||||
{
|
||||
const GSVertex* v = &m_vertex->buff[m_index->buff[i]];
|
||||
const GSVertex* v = &vtx_buff.buff[idx_buff.buff[i]];
|
||||
|
||||
if (prim == GS_SPRITE && (i & 1))
|
||||
{
|
||||
@@ -5740,32 +5752,33 @@ __forceinline void GSState::HandleAutoFlush()
|
||||
}
|
||||
}
|
||||
|
||||
bool GSState::CheckOverlapVerts(u32 n)
|
||||
__inline bool GSState::CheckOverlapVerts(u32 n)
|
||||
{
|
||||
if (!GSConfig.UserHacks_DrawBuffering)
|
||||
return false;
|
||||
GSVertexBuff& vtx_buff = *m_vertex;
|
||||
GSIndexBuff& idx_buff = *m_index;
|
||||
|
||||
if (m_recent_buffer_switch && ((m_vertex->tail + 1) - m_vertex->head) == n)
|
||||
if (m_recent_buffer_switch && ((vtx_buff.tail + 1) - vtx_buff.head) == n)
|
||||
{
|
||||
m_recent_buffer_switch = false;
|
||||
|
||||
if (m_used_buffers_idx > 1)
|
||||
{
|
||||
const GSVertex* v = &m_vertex->buff[0];
|
||||
const GSVertex* RESTRICT v = &vtx_buff.buff[0];
|
||||
const GSVector2i off_xy = GSVector2i(m_context->XYOFFSET.OFX, m_context->XYOFFSET.OFY);
|
||||
GSVector2i cur_verts[3];
|
||||
|
||||
GSVector4i new_area = GSVector4i(m_v.XYZ.X - m_context->XYOFFSET.OFX, m_v.XYZ.Y - m_context->XYOFFSET.OFY).xyxy();
|
||||
GSVector4i new_area = GSVector4i(m_v.XYZ.X - off_xy.x, m_v.XYZ.Y - off_xy.y).xyxy();
|
||||
cur_verts[0] = GSVector2i(new_area.x, new_area.y);
|
||||
|
||||
for (u32 i = 0; i < (n - 1); i++)
|
||||
{
|
||||
const int pos = (m_vertex->tail - 1) - i;
|
||||
const int pos = (vtx_buff.tail - 1) - i;
|
||||
|
||||
GSVector2i prev_vert;
|
||||
if (m_env.PRIM.PRIM == GS_TRIANGLEFAN && i == (n - 2))
|
||||
prev_vert = GSVector2i(v[m_vertex->head].XYZ.X - m_context->XYOFFSET.OFX, v[m_vertex->head].XYZ.X - m_context->XYOFFSET.OFY);
|
||||
prev_vert = GSVector2i(v[vtx_buff.head].XYZ.X - off_xy.y, v[vtx_buff.head].XYZ.X - off_xy.y);
|
||||
else
|
||||
prev_vert = GSVector2i(v[pos].XYZ.X - m_context->XYOFFSET.OFX, v[pos].XYZ.Y - m_context->XYOFFSET.OFY);
|
||||
prev_vert = GSVector2i(v[pos].XYZ.X - off_xy.x, v[pos].XYZ.Y - off_xy.y);
|
||||
|
||||
cur_verts[i + 1] = prev_vert;
|
||||
|
||||
@@ -5775,13 +5788,13 @@ bool GSState::CheckOverlapVerts(u32 n)
|
||||
new_area.w = std::max(new_area.w, prev_vert.y);
|
||||
}
|
||||
|
||||
if (m_index->tail > 0)
|
||||
if (idx_buff.tail > 0)
|
||||
{
|
||||
u32 matching_verts = 0;
|
||||
for (u32 i = 0; i < n; i++)
|
||||
{
|
||||
const u32 pos = m_index->buff[(m_index->tail - n) + i];
|
||||
const GSVector2i prev_vert = GSVector2i(v[pos].XYZ.X - m_context->XYOFFSET.OFX, v[pos].XYZ.Y - m_context->XYOFFSET.OFY);
|
||||
const u32 pos = idx_buff.buff[(idx_buff.tail - n) + i];
|
||||
const GSVector2i prev_vert = GSVector2i(v[pos].XYZ.X - off_xy.x, v[pos].XYZ.Y - off_xy.y);
|
||||
|
||||
for (u32 j = 0; j < n; j++)
|
||||
{
|
||||
@@ -5810,48 +5823,6 @@ bool GSState::CheckOverlapVerts(u32 n)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/*const GSVertex* v = &m_vertex->buff[0];
|
||||
|
||||
GSVector4i new_area = GSVector4i(m_v.XYZ.X - m_context->XYOFFSET.OFX, m_v.XYZ.Y - m_context->XYOFFSET.OFY).xyxy();
|
||||
for (u32 i = 0; i < (n - 1); i++)
|
||||
{
|
||||
const int pos = m_index->buff[(m_index->tail - 1) - i];
|
||||
GSVector2i pre_vert = GSVector2i(v[pos].XYZ.X - m_context->XYOFFSET.OFX, v[pos].XYZ.Y - m_context->XYOFFSET.OFY);
|
||||
new_area.x = std::min(new_area.x, pre_vert.x);
|
||||
new_area.z = std::max(new_area.z, pre_vert.x);
|
||||
new_area.y = std::min(new_area.y, pre_vert.y);
|
||||
new_area.w = std::max(new_area.w, pre_vert.y);
|
||||
}
|
||||
new_area = new_area.sra32<4>();
|
||||
|
||||
if (new_area.rintersect(temp_draw_rect).eq(new_area))
|
||||
{
|
||||
const int end_pos = m_index->tail - (n - 1);
|
||||
//Need to check if it's already drawn at this vector with this setup, if it has, it means one of the other draws might be drawing over it, which is a bad time for us, so best check.
|
||||
for (int j = 0; j < end_pos; j+=n)
|
||||
{
|
||||
if (v[m_index->buff[j]].XYZ.X == m_v.XYZ.X && v[m_index->buff[j]].XYZ.Y == m_v.XYZ.Y)
|
||||
{
|
||||
int min_point = std::max(j - 2, 0);
|
||||
int match = 0;
|
||||
|
||||
for (int k = min_point; k < (min_point + 5); k++)
|
||||
{
|
||||
if (k == j)
|
||||
continue;
|
||||
|
||||
if (v[m_index->buff[k]].XYZ.X == v[m_vertex->tail - 2].XYZ.X && v[m_index->buff[k]].XYZ.Y == v[m_vertex->tail - 2].XYZ.Y)
|
||||
match |= 1;
|
||||
if (v[m_index->buff[k]].XYZ.X == v[m_vertex->tail - 1].XYZ.X && v[m_index->buff[k]].XYZ.Y == v[m_vertex->tail - 1].XYZ.Y)
|
||||
match |= 2;
|
||||
}
|
||||
|
||||
if (match)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -5861,28 +5832,30 @@ __forceinline void GSState::VertexKick(u32 skip)
|
||||
{
|
||||
constexpr u32 n = NumIndicesForPrim(prim);
|
||||
constexpr int primclass = GSUtil::GetPrimClass(prim);
|
||||
GSVertexBuff& vtx_buff = *m_vertex;
|
||||
GSIndexBuff& idx_buff = *m_index;
|
||||
static_assert(n > 0);
|
||||
|
||||
pxAssert(m_vertex->tail < m_vertex->maxcount + 3);
|
||||
pxAssert(vtx_buff.tail < vtx_buff. maxcount + 3);
|
||||
|
||||
if constexpr (prim == GS_INVALID)
|
||||
{
|
||||
m_vertex->tail = m_vertex->head;
|
||||
vtx_buff.tail = vtx_buff.head;
|
||||
return;
|
||||
}
|
||||
|
||||
if (CheckOverlapVerts(n))
|
||||
Flush(CONTEXTCHANGE);
|
||||
if (GSConfig.UserHacks_DrawBuffering)
|
||||
if (CheckOverlapVerts(n))
|
||||
Flush(CONTEXTCHANGE);
|
||||
|
||||
if (auto_flush && skip == 0 && m_index->tail > 0 && ((m_vertex->tail + 1) - m_vertex->head) >= n)
|
||||
if (auto_flush && skip == 0 && idx_buff.tail > 0 && ((vtx_buff.tail + 1) - vtx_buff.head) >= n)
|
||||
{
|
||||
HandleAutoFlush<prim>();
|
||||
}
|
||||
|
||||
u32 head = m_vertex->head;
|
||||
u32 tail = m_vertex->tail;
|
||||
u32 next = m_vertex->next;
|
||||
u32 xy_tail = m_vertex->xy_tail;
|
||||
u32 head = vtx_buff.head;
|
||||
u32 tail = vtx_buff.tail;
|
||||
u32 next = vtx_buff.next;
|
||||
u32 xy_tail = vtx_buff.xy_tail;
|
||||
|
||||
if (GSIsHardwareRenderer() && GSLocalMemory::m_psm[m_context->ZBUF.PSM].bpp == 32)
|
||||
{
|
||||
@@ -5897,7 +5870,7 @@ __forceinline void GSState::VertexKick(u32 skip)
|
||||
const GSVector4i new_v0(m_v.m[0]);
|
||||
const GSVector4i new_v1(m_v.m[1]);
|
||||
|
||||
GSVector4i* RESTRICT tailptr = (GSVector4i*)&m_vertex->buff[tail];
|
||||
GSVector4i* RESTRICT tailptr = (GSVector4i*)&vtx_buff.buff[tail];
|
||||
|
||||
tailptr[0] = new_v0;
|
||||
tailptr[1] = new_v1;
|
||||
@@ -5905,41 +5878,29 @@ __forceinline void GSState::VertexKick(u32 skip)
|
||||
// We maintain the X/Y coordinates for the last 4 vertices, as well as the head for triangle fans, so we can compute
|
||||
// the min/max, and cull degenerate triangles, which saves draws in some cases. Why 4? Mod 4 is cheaper than Mod 3.
|
||||
const GSVector4i xy = new_v1.xxxx().u16to32().sub32(m_xyof);
|
||||
m_vertex->xy[xy_tail & 3] = xy;
|
||||
vtx_buff.xy[xy_tail & 3] = xy;
|
||||
|
||||
// Backup head for triangle fans so we can read it later, otherwise it'll get lost after the 4th vertex.
|
||||
if (prim == GS_TRIANGLEFAN && tail == head)
|
||||
m_vertex->xyhead = xy;
|
||||
vtx_buff.xyhead = xy;
|
||||
|
||||
m_vertex->tail = ++tail;
|
||||
m_vertex->xy_tail = ++xy_tail;
|
||||
vtx_buff.tail = ++tail;
|
||||
vtx_buff.xy_tail = ++xy_tail;
|
||||
|
||||
const u32 m = tail - head;
|
||||
|
||||
if (m < n)
|
||||
return;
|
||||
|
||||
if (m_index->tail == 0/* && ((m_backed_up_ctx != m_env.PRIM.CTXT) || m_dirty_gs_regs)*/)
|
||||
{
|
||||
const int ctx = m_env.PRIM.CTXT;
|
||||
std::memcpy(&m_prev_env, &m_env, 88);
|
||||
std::memcpy(&m_prev_env.CTXT[ctx], &m_env.CTXT[ctx], 96);
|
||||
std::memcpy(&m_prev_env.CTXT[ctx].offset, &m_env.CTXT[ctx].offset, sizeof(m_env.CTXT[ctx].offset));
|
||||
std::memcpy(&m_prev_env.CTXT[ctx].scissor, &m_env.CTXT[ctx].scissor, sizeof(m_env.CTXT[ctx].scissor));
|
||||
m_dirty_gs_regs = 0;
|
||||
m_backed_up_ctx = m_env.PRIM.CTXT;
|
||||
SetDrawBufferEnv();
|
||||
}
|
||||
|
||||
// Skip draws when scissor is out of range (i.e. bottom-right is less than top-left), since everything will get clipped.
|
||||
skip |= static_cast<u32>(m_scissor_invalid);
|
||||
|
||||
GSVector4i bbox;
|
||||
if (skip == 0)
|
||||
{
|
||||
const GSVector4i v0 = m_vertex->xy[(xy_tail - 1) & 3];
|
||||
const GSVector4i v1 = m_vertex->xy[(xy_tail - 2) & 3];
|
||||
const GSVector4i v2 = (prim == GS_TRIANGLEFAN) ? m_vertex->xyhead : m_vertex->xy[(xy_tail - 3) & 3];
|
||||
const GSVector4i v0 = vtx_buff.xy[(xy_tail - 1) & 3];
|
||||
const GSVector4i v1 = vtx_buff.xy[(xy_tail - 2) & 3];
|
||||
const GSVector4i v2 = (prim == GS_TRIANGLEFAN) ? vtx_buff.xyhead : vtx_buff.xy[(xy_tail - 3) & 3];
|
||||
|
||||
if constexpr (n == 1)
|
||||
{
|
||||
@@ -6004,14 +5965,14 @@ __forceinline void GSState::VertexKick(u32 skip)
|
||||
case GS_LINELIST:
|
||||
case GS_TRIANGLELIST:
|
||||
case GS_SPRITE:
|
||||
m_vertex->tail = head; // no need to check or grow the buffer length
|
||||
vtx_buff.tail = head; // no need to check or grow the buffer length
|
||||
break;
|
||||
case GS_LINESTRIP:
|
||||
case GS_TRIANGLESTRIP:
|
||||
m_vertex->head = head + 1;
|
||||
vtx_buff.head = head + 1;
|
||||
[[fallthrough]];
|
||||
case GS_TRIANGLEFAN:
|
||||
if (tail >= m_vertex->maxcount)
|
||||
if (tail >= vtx_buff.maxcount)
|
||||
GrowVertexBuffer(); // in case too many vertices were skipped
|
||||
break;
|
||||
default:
|
||||
@@ -6021,71 +5982,85 @@ __forceinline void GSState::VertexKick(u32 skip)
|
||||
return;
|
||||
}
|
||||
|
||||
if (tail >= m_vertex->maxcount)
|
||||
if (idx_buff.tail == 0 /* && ((m_backed_up_ctx != m_env.PRIM.CTXT) || m_dirty_gs_regs)*/)
|
||||
{
|
||||
const int ctx = m_env.PRIM.CTXT;
|
||||
std::memcpy(&m_prev_env, &m_env, 88);
|
||||
std::memcpy(&m_prev_env.CTXT[ctx], &m_env.CTXT[ctx], 96);
|
||||
std::memcpy(&m_prev_env.CTXT[ctx].offset, &m_env.CTXT[ctx].offset, sizeof(m_env.CTXT[ctx].offset));
|
||||
std::memcpy(&m_prev_env.CTXT[ctx].scissor, &m_env.CTXT[ctx].scissor, sizeof(m_env.CTXT[ctx].scissor));
|
||||
m_dirty_gs_regs = 0;
|
||||
m_backed_up_ctx = m_env.PRIM.CTXT;
|
||||
|
||||
if (GSConfig.UserHacks_DrawBuffering)
|
||||
SetDrawBufferEnv();
|
||||
}
|
||||
|
||||
if (tail >= vtx_buff.maxcount)
|
||||
GrowVertexBuffer();
|
||||
|
||||
u16* RESTRICT buff = &m_index->buff[m_index->tail];
|
||||
u16* RESTRICT buff = &idx_buff.buff[idx_buff.tail];
|
||||
|
||||
switch (prim)
|
||||
{
|
||||
case GS_POINTLIST:
|
||||
buff[0] = static_cast<u16>(head + 0);
|
||||
m_vertex->head = head + 1;
|
||||
m_vertex->next = head + 1;
|
||||
m_index->tail += 1;
|
||||
vtx_buff.head = head + 1;
|
||||
vtx_buff.next = head + 1;
|
||||
idx_buff.tail += 1;
|
||||
break;
|
||||
case GS_LINELIST:
|
||||
buff[0] = static_cast<u16>(head + 0);
|
||||
buff[1] = static_cast<u16>(head + 1);
|
||||
m_vertex->head = head + 2;
|
||||
m_vertex->next = head + 2;
|
||||
m_index->tail += 2;
|
||||
vtx_buff.head = head + 2;
|
||||
vtx_buff.next = head + 2;
|
||||
idx_buff.tail += 2;
|
||||
break;
|
||||
case GS_LINESTRIP:
|
||||
if (next < head)
|
||||
{
|
||||
m_vertex->buff[next + 0] = m_vertex->buff[head + 0];
|
||||
m_vertex->buff[next + 1] = m_vertex->buff[head + 1];
|
||||
vtx_buff.buff[next + 0] = vtx_buff.buff[head + 0];
|
||||
vtx_buff.buff[next + 1] = vtx_buff.buff[head + 1];
|
||||
head = next;
|
||||
m_vertex->tail = next + 2;
|
||||
vtx_buff.tail = next + 2;
|
||||
}
|
||||
buff[0] = static_cast<u16>(head + 0);
|
||||
buff[1] = static_cast<u16>(head + 1);
|
||||
m_vertex->head = head + 1;
|
||||
m_vertex->next = head + 2;
|
||||
m_index->tail += 2;
|
||||
vtx_buff.head = head + 1;
|
||||
vtx_buff.next = head + 2;
|
||||
idx_buff.tail += 2;
|
||||
break;
|
||||
case GS_TRIANGLELIST:
|
||||
buff[0] = static_cast<u16>(head + 0);
|
||||
buff[1] = static_cast<u16>(head + 1);
|
||||
buff[2] = static_cast<u16>(head + 2);
|
||||
m_vertex->head = head + 3;
|
||||
m_vertex->next = head + 3;
|
||||
m_index->tail += 3;
|
||||
vtx_buff.head = head + 3;
|
||||
vtx_buff.next = head + 3;
|
||||
idx_buff.tail += 3;
|
||||
break;
|
||||
case GS_TRIANGLESTRIP:
|
||||
if (next < head)
|
||||
{
|
||||
m_vertex->buff[next + 0] = m_vertex->buff[head + 0];
|
||||
m_vertex->buff[next + 1] = m_vertex->buff[head + 1];
|
||||
m_vertex->buff[next + 2] = m_vertex->buff[head + 2];
|
||||
vtx_buff.buff[next + 0] = vtx_buff.buff[head + 0];
|
||||
vtx_buff.buff[next + 1] = vtx_buff.buff[head + 1];
|
||||
vtx_buff.buff[next + 2] = vtx_buff.buff[head + 2];
|
||||
head = next;
|
||||
m_vertex->tail = next + 3;
|
||||
vtx_buff.tail = next + 3;
|
||||
}
|
||||
buff[0] = static_cast<u16>(head + 0);
|
||||
buff[1] = static_cast<u16>(head + 1);
|
||||
buff[2] = static_cast<u16>(head + 2);
|
||||
m_vertex->head = head + 1;
|
||||
m_vertex->next = head + 3;
|
||||
m_index->tail += 3;
|
||||
vtx_buff.head = head + 1;
|
||||
vtx_buff.next = head + 3;
|
||||
idx_buff.tail += 3;
|
||||
break;
|
||||
case GS_TRIANGLEFAN:
|
||||
// TODO: remove gaps, next == head && head < tail - 3 || next > head && next < tail - 2 (very rare)
|
||||
buff[0] = static_cast<u16>(head + 0);
|
||||
buff[1] = static_cast<u16>(tail - 2);
|
||||
buff[2] = static_cast<u16>(tail - 1);
|
||||
m_vertex->next = tail;
|
||||
m_index->tail += 3;
|
||||
vtx_buff.next = tail;
|
||||
idx_buff.tail += 3;
|
||||
break;
|
||||
case GS_SPRITE:
|
||||
buff[0] = static_cast<u16>(head + 0);
|
||||
@@ -6093,11 +6068,11 @@ __forceinline void GSState::VertexKick(u32 skip)
|
||||
|
||||
// Update the first vert's Q for ease of doing Autoflush
|
||||
if (!m_env.PRIM.FST)
|
||||
m_vertex->buff[buff[0]].RGBAQ.Q = m_vertex->buff[buff[1]].RGBAQ.Q;
|
||||
vtx_buff.buff[buff[0]].RGBAQ.Q = vtx_buff.buff[buff[1]].RGBAQ.Q;
|
||||
|
||||
m_vertex->head = head + 2;
|
||||
m_vertex->next = head + 2;
|
||||
m_index->tail += 2;
|
||||
vtx_buff.head = head + 2;
|
||||
vtx_buff.next = head + 2;
|
||||
idx_buff.tail += 2;
|
||||
break;
|
||||
default:
|
||||
ASSUME(0);
|
||||
@@ -6105,14 +6080,14 @@ __forceinline void GSState::VertexKick(u32 skip)
|
||||
|
||||
// Update rectangle for the current draw. Needs exclusive endpoints.
|
||||
const GSVector4i draw_rect = bbox.sra32<4>() + GSVector4i(0, 0, 1, 1);
|
||||
if (m_index->tail != n)
|
||||
if (idx_buff.tail != n)
|
||||
temp_draw_rect = temp_draw_rect.runion(draw_rect);
|
||||
else
|
||||
temp_draw_rect = draw_rect;
|
||||
temp_draw_rect = temp_draw_rect.rintersect(m_context->scissor.in);
|
||||
|
||||
constexpr u32 max_vertices = MaxVerticesForPrim(prim);
|
||||
if (max_vertices != 0 && m_vertex->tail >= max_vertices)
|
||||
if (max_vertices != 0 && vtx_buff.tail >= max_vertices)
|
||||
Flush(VERTEXCOUNT);
|
||||
}
|
||||
|
||||
@@ -6274,7 +6249,6 @@ GSState::TextureMinMaxResult GSState::GetTextureMinMax(GIFRegTEX0 TEX0, GIFRegCL
|
||||
const GSVector4i scissored_rc(int_rc.rintersect(m_context->scissor.in));
|
||||
if (!int_rc.eq(scissored_rc))
|
||||
{
|
||||
|
||||
const GSVertex* vert_first = &m_vertex->buff[m_index->buff[0]];
|
||||
const GSVertex* vert_second = &m_vertex->buff[m_index->buff[1]];
|
||||
const GSVertex* vert_third = &m_vertex->buff[m_index->buff[2]];
|
||||
|
||||
+1
-1
@@ -506,7 +506,7 @@ public:
|
||||
|
||||
virtual void Move();
|
||||
|
||||
GSVector4i GetTEX0Rect(GSDrawingContext prev_ctx);
|
||||
GSVector4i GetTEX0Rect(const GSDrawingContext& prev_ctx);
|
||||
void CheckWriteOverlap(bool req_write, bool req_read);
|
||||
void Write(const u8* mem, int len);
|
||||
void Read(u8* mem, int len);
|
||||
|
||||
@@ -222,7 +222,9 @@ void GSRendererHW::Lines2Sprites()
|
||||
|
||||
// each sprite converted to quad needs twice the space
|
||||
|
||||
while (m_vertex->tail * 2 > m_vertex->maxcount)
|
||||
const int tail = m_vertex->tail * 2;
|
||||
const int max_count = m_vertex->maxcount;
|
||||
while (tail > max_count)
|
||||
{
|
||||
GrowVertexBuffer();
|
||||
}
|
||||
@@ -298,12 +300,13 @@ void GSRendererHW::Lines2Sprites()
|
||||
|
||||
void GSRendererHW::ExpandLineIndices()
|
||||
{
|
||||
const u32 process_count = (m_index->tail + 7) / 8 * 8;
|
||||
GSIndexBuff& idx_buff = *m_index;
|
||||
const u32 process_count = (idx_buff.tail + 7) / 8 * 8;
|
||||
constexpr u32 expansion_factor = 3;
|
||||
m_index->tail *= expansion_factor;
|
||||
GSVector4i* end = reinterpret_cast<GSVector4i*>(m_index->buff);
|
||||
GSVector4i* read = reinterpret_cast<GSVector4i*>(m_index->buff + process_count);
|
||||
GSVector4i* write = reinterpret_cast<GSVector4i*>(m_index->buff + process_count * expansion_factor);
|
||||
idx_buff.tail *= expansion_factor;
|
||||
GSVector4i* end = reinterpret_cast<GSVector4i*>(idx_buff.buff);
|
||||
GSVector4i* read = reinterpret_cast<GSVector4i*>(idx_buff.buff + process_count);
|
||||
GSVector4i* write = reinterpret_cast<GSVector4i*>(idx_buff.buff + process_count * expansion_factor);
|
||||
|
||||
constexpr GSVector4i mask0 = GSVector4i::cxpr8(0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 2, 3, 4, 5, 4, 5);
|
||||
constexpr GSVector4i mask1 = GSVector4i::cxpr8(6, 7, 4, 5, 6, 7, 6, 7, 8, 9, 8, 9, 10, 11, 8, 9);
|
||||
@@ -1428,7 +1431,8 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
|
||||
}
|
||||
else
|
||||
{
|
||||
for (u32 i = 2; i < (m_vertex->tail & ~1); i++)
|
||||
const u32 tail = m_vertex->tail & ~1;
|
||||
for (u32 i = 2; i < tail; i++)
|
||||
{
|
||||
bool unique_found = false;
|
||||
|
||||
@@ -1469,7 +1473,8 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
|
||||
}
|
||||
else
|
||||
{
|
||||
for (u32 i = 2; i < (m_vertex->tail & ~1); i++)
|
||||
const u32 tail = m_vertex->tail & ~1;
|
||||
for (u32 i = 2; i < tail; i++)
|
||||
{
|
||||
bool unique_found = false;
|
||||
|
||||
@@ -2235,16 +2240,16 @@ void GSRendererHW::HandleManualDeswizzle()
|
||||
// Check if it's doing manual deswizzling first (draws are 32x16), if they are, check if the Z is flat, if not,
|
||||
// we're gonna have to get creative and swap around the quandrants, but that's a TODO.
|
||||
GSVertex* v = &m_vertex->buff[0];
|
||||
|
||||
GSIndexBuff& idx_buff = *m_index;
|
||||
// Check for page quadrant and compare it to the quadrant from the verts, if it does match then we need to do correction.
|
||||
const GSVector2i page_quadrant = GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].pgs / 2;
|
||||
|
||||
if (PRIM->FST)
|
||||
{
|
||||
for (u32 i = 0; i < m_index->tail; i += 2)
|
||||
for (u32 i = 0; i < idx_buff.tail; i += 2)
|
||||
{
|
||||
const u32 index_first = m_index->buff[i];
|
||||
const u32 index_last = m_index->buff[i + 1];
|
||||
const u32 index_first = idx_buff.buff[i];
|
||||
const u32 index_last = idx_buff.buff[i + 1];
|
||||
|
||||
if ((abs((v[index_last].U) - (v[index_first].U)) >> 4) != page_quadrant.x || (abs((v[index_last].V) - (v[index_first].V)) >> 4) != page_quadrant.y)
|
||||
return;
|
||||
@@ -2252,10 +2257,10 @@ void GSRendererHW::HandleManualDeswizzle()
|
||||
}
|
||||
else
|
||||
{
|
||||
for (u32 i = 0; i < m_index->tail; i += 2)
|
||||
for (u32 i = 0; i < idx_buff.tail; i += 2)
|
||||
{
|
||||
const u32 index_first = m_index->buff[i];
|
||||
const u32 index_last = m_index->buff[i + 1];
|
||||
const u32 index_first = idx_buff.buff[i];
|
||||
const u32 index_last = idx_buff.buff[i + 1];
|
||||
const u32 x = abs(((v[index_last].ST.S / v[index_last].RGBAQ.Q) * (1 << m_context->TEX0.TW)) - ((v[index_first].ST.S / v[index_first].RGBAQ.Q) * (1 << m_context->TEX0.TW)));
|
||||
const u32 y = abs(((v[index_last].ST.T / v[index_last].RGBAQ.Q) * (1 << m_context->TEX0.TH)) - ((v[index_first].ST.T / v[index_first].RGBAQ.Q) * (1 << m_context->TEX0.TH)));
|
||||
|
||||
@@ -2756,7 +2761,8 @@ void GSRendererHW::RoundSpriteOffset()
|
||||
void GSRendererHW::Draw()
|
||||
{
|
||||
static u32 num_skipped_channel_shuffle_draws = 0;
|
||||
|
||||
GSVertexBuff& vtx_buff = *m_vertex;
|
||||
GSIndexBuff& idx_buff = *m_index;
|
||||
// We mess with this state as an optimization, so take a copy and use that instead.
|
||||
const GSDrawingContext* context = m_context;
|
||||
m_cached_ctx.TEX0 = context->TEX0;
|
||||
@@ -3183,7 +3189,7 @@ void GSRendererHW::Draw()
|
||||
}
|
||||
|
||||
const u32 vert_index = (m_vt.m_primclass == GS_TRIANGLE_CLASS) ? 2 : 1;
|
||||
u32 const_color = m_vertex->buff[m_index->buff[vert_index]].RGBAQ.U32[0];
|
||||
u32 const_color = vtx_buff.buff[idx_buff.buff[vert_index]].RGBAQ.U32[0];
|
||||
u32 fb_mask = m_cached_ctx.FRAME.FBMSK;
|
||||
|
||||
// If we could just check the colour, it would be great, but Echo Night decided it's going to set the alpha and green to 128, for some reason, and actually be 32bit, so it ruined my day.
|
||||
@@ -3219,7 +3225,7 @@ void GSRendererHW::Draw()
|
||||
m_cached_ctx.TEXA.TA0 = 0;
|
||||
m_cached_ctx.TEXA.TA1 = 128;
|
||||
m_cached_ctx.FRAME.PSM = (m_cached_ctx.FRAME.PSM & 2) ? m_cached_ctx.FRAME.PSM : PSMCT16;
|
||||
m_vertex->buff[m_index->buff[1]].RGBAQ.U32[0] = const_color;
|
||||
vtx_buff.buff[idx_buff.buff[1]].RGBAQ.U32[0] = const_color;
|
||||
ReplaceVerticesWithSprite(m_r, GSVector2i(m_r.width(), m_r.height()));
|
||||
}
|
||||
|
||||
@@ -3444,13 +3450,13 @@ void GSRendererHW::Draw()
|
||||
const u32 page_alignment = GSLocalMemory::IsPageAlignedMasked(m_cached_ctx.TEX0.PSM, m_r);
|
||||
const bool page_aligned = (page_alignment & 0xF0F0) != 0; // Make sure Y is page aligned.
|
||||
if (!no_rt && page_aligned && m_cached_ctx.ZBUF.ZMSK && GSLocalMemory::m_psm[m_cached_ctx.FRAME.PSM].bpp == 16 && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].trbpp <= 16 &&
|
||||
(m_vt.m_primclass == GS_SPRITE_CLASS || (m_vt.m_primclass == GS_TRIANGLE_CLASS && (m_index->tail % 6) == 0 && TrianglesAreQuads(true) && m_index->tail > 6)))
|
||||
(m_vt.m_primclass == GS_SPRITE_CLASS || (m_vt.m_primclass == GS_TRIANGLE_CLASS && (idx_buff.tail % 6) == 0 && TrianglesAreQuads(true) && idx_buff.tail > 6)))
|
||||
{
|
||||
// Tail check is to make sure we have enough strips to go all the way across the page, or if it's using a region clamp could be used to draw strips.
|
||||
if (GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].bpp == 16 &&
|
||||
(m_index->tail >= (m_cached_ctx.TEX0.TBW * 2) || m_cached_ctx.TEX0.TBP0 == m_cached_ctx.FRAME.Block() || m_cached_ctx.CLAMP.WMS > CLAMP_CLAMP || m_cached_ctx.CLAMP.WMT > CLAMP_CLAMP))
|
||||
(idx_buff.tail >= (m_cached_ctx.TEX0.TBW * 2) || m_cached_ctx.TEX0.TBP0 == m_cached_ctx.FRAME.Block() || m_cached_ctx.CLAMP.WMS > CLAMP_CLAMP || m_cached_ctx.CLAMP.WMT > CLAMP_CLAMP))
|
||||
{
|
||||
const GSVertex* v = &m_vertex->buff[0];
|
||||
const GSVertex* v = &vtx_buff.buff[0];
|
||||
|
||||
const int first_x = std::clamp((static_cast<int>(((v[0].XYZ.X - m_context->XYOFFSET.OFX) + 8))) >> 4, 0, 2048);
|
||||
const bool offset_last = PRIM->FST ? (v[1].U > v[0].U) : ((v[1].ST.S / v[1].RGBAQ.Q) > (v[0].ST.S / v[1].RGBAQ.Q));
|
||||
@@ -3476,11 +3482,11 @@ void GSRendererHW::Draw()
|
||||
{
|
||||
bool shuffle_channel_reads = !m_cached_ctx.FRAME.FBMSK;
|
||||
const u32 increment = (m_vt.m_primclass == GS_TRIANGLE_CLASS) ? 3 : 2;
|
||||
const GSVertex* v = &m_vertex->buff[0];
|
||||
const GSVertex* v = &vtx_buff.buff[0];
|
||||
|
||||
if (shuffle_channel_reads)
|
||||
{
|
||||
for (u32 i = 0; i < m_index->tail; i += increment)
|
||||
for (u32 i = 0; i < idx_buff.tail; i += increment)
|
||||
{
|
||||
const int first_u = (PRIM->FST ? v[i].U : static_cast<int>(v[i].ST.S / v[(increment == 2) ? i + 1 : i].RGBAQ.Q)) >> 4;
|
||||
const int second_u = (PRIM->FST ? v[i + 1].U : static_cast<int>(v[i + 1].ST.S / v[i + 1].RGBAQ.Q)) >> 4;
|
||||
@@ -3829,9 +3835,10 @@ void GSRendererHW::Draw()
|
||||
|
||||
if (vertical_offset || horizontal_offset)
|
||||
{
|
||||
GSVertex* v = &m_vertex->buff[0];
|
||||
GSVertex* v = &vtx_buff.buff[0];
|
||||
const u32 tail = vtx_buff.tail;
|
||||
|
||||
for (u32 i = 0; i < m_vertex->tail; i++)
|
||||
for (u32 i = 0; i < tail; i++)
|
||||
{
|
||||
v[i].XYZ.X += horizontal_offset << 4;
|
||||
v[i].XYZ.Y += vertical_offset << 4;
|
||||
@@ -4126,9 +4133,10 @@ void GSRendererHW::Draw()
|
||||
|
||||
if (vertical_offset || horizontal_offset)
|
||||
{
|
||||
GSVertex* v = &m_vertex->buff[0];
|
||||
GSVertex* v = &vtx_buff.buff[0];
|
||||
const u32 tail = vtx_buff.tail;
|
||||
|
||||
for (u32 i = 0; i < m_vertex->tail; i++)
|
||||
for (u32 i = 0; i < tail; i++)
|
||||
{
|
||||
v[i].XYZ.X += horizontal_offset << 4;
|
||||
v[i].XYZ.Y += vertical_offset << 4;
|
||||
@@ -4587,13 +4595,13 @@ void GSRendererHW::Draw()
|
||||
if (!m_texture_shuffle && !m_channel_shuffle)
|
||||
{
|
||||
// Try to turn blits in to single sprites, saves upscaling problems when striped clears/blits.
|
||||
if (m_vt.m_primclass == GS_SPRITE_CLASS && m_primitive_covers_without_gaps == NoGapsType::FullCover && m_index->tail > 2 && (!PRIM->TME || TextureCoversWithoutGapsNotEqual()) && m_vt.m_eq.rgba == 0xFFFF)
|
||||
if (m_vt.m_primclass == GS_SPRITE_CLASS && m_primitive_covers_without_gaps == NoGapsType::FullCover && idx_buff.tail > 2 && (!PRIM->TME || TextureCoversWithoutGapsNotEqual()) && m_vt.m_eq.rgba == 0xFFFF)
|
||||
{
|
||||
// Full final framebuffer only.
|
||||
const GSVector2i fb_size = PCRTCDisplays.GetFramebufferSize(-1);
|
||||
if (std::abs(fb_size.x - m_r.width()) <= 1 && std::abs(fb_size.y - m_r.height()) <= 1)
|
||||
{
|
||||
GSVertex* v = m_vertex->buff;
|
||||
GSVertex* v = vtx_buff.buff;
|
||||
|
||||
v[0].XYZ.Z = v[1].XYZ.Z;
|
||||
v[0].RGBAQ = v[1].RGBAQ;
|
||||
@@ -4602,23 +4610,23 @@ void GSRendererHW::Draw()
|
||||
m_vt.m_eq.z = true;
|
||||
m_vt.m_eq.f = true;
|
||||
|
||||
v[1].XYZ.X = v[m_index->tail - 1].XYZ.X;
|
||||
v[1].XYZ.Y = v[m_index->tail - 1].XYZ.Y;
|
||||
v[1].XYZ.X = v[idx_buff.tail - 1].XYZ.X;
|
||||
v[1].XYZ.Y = v[idx_buff.tail - 1].XYZ.Y;
|
||||
|
||||
if (PRIM->FST)
|
||||
{
|
||||
v[1].U = v[m_index->tail - 1].U;
|
||||
v[1].V = v[m_index->tail - 1].V;
|
||||
v[1].U = v[idx_buff.tail - 1].U;
|
||||
v[1].V = v[idx_buff.tail - 1].V;
|
||||
}
|
||||
else
|
||||
{
|
||||
v[1].ST.S = v[m_index->tail - 1].ST.S;
|
||||
v[1].ST.T = v[m_index->tail - 1].ST.T;
|
||||
v[1].RGBAQ.Q = v[m_index->tail - 1].RGBAQ.Q;
|
||||
v[1].ST.S = v[idx_buff.tail - 1].ST.S;
|
||||
v[1].ST.T = v[idx_buff.tail - 1].ST.T;
|
||||
v[1].RGBAQ.Q = v[idx_buff.tail - 1].RGBAQ.Q;
|
||||
}
|
||||
|
||||
m_vertex->head = m_vertex->tail = m_vertex->next = 2;
|
||||
m_index->tail = 2;
|
||||
vtx_buff.head = vtx_buff.tail = vtx_buff.next = 2;
|
||||
idx_buff.tail = 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5076,8 +5084,8 @@ void GSRendererHW::Draw()
|
||||
// but it still needs to adjust native stuff from memory as it's not been compensated for upscaling (Dragon Quest 8 font for example).
|
||||
if (CanUpscale() && (m_vt.m_primclass == GS_SPRITE_CLASS) && rt && rt->GetScale() > 1.0f)
|
||||
{
|
||||
const u32 count = m_vertex->next;
|
||||
GSVertex* v = &m_vertex->buff[0];
|
||||
const u32 count = vtx_buff.next;
|
||||
GSVertex* v = &vtx_buff.buff[0];
|
||||
|
||||
// Hack to avoid vertical black line in various games (ace combat/tekken)
|
||||
if (GSConfig.UserHacks_AlignSpriteX)
|
||||
@@ -5304,33 +5312,35 @@ void GSRendererHW::Draw()
|
||||
/// Verifies assumptions we expect to hold about indices
|
||||
bool GSRendererHW::VerifyIndices()
|
||||
{
|
||||
GSIndexBuff& idx_buff = *m_index;
|
||||
|
||||
switch (m_vt.m_primclass)
|
||||
{
|
||||
case GS_SPRITE_CLASS:
|
||||
if (m_index->tail % 2 != 0)
|
||||
if (idx_buff.tail % 2 != 0)
|
||||
return false;
|
||||
[[fallthrough]];
|
||||
case GS_POINT_CLASS:
|
||||
// Expect indices to be flat increasing
|
||||
for (u32 i = 0; i < m_index->tail; i++)
|
||||
for (u32 i = 0; i < idx_buff.tail; i++)
|
||||
{
|
||||
if (m_index->buff[i] != i)
|
||||
if (idx_buff.buff[i] != i)
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case GS_LINE_CLASS:
|
||||
if (m_index->tail % 2 != 0)
|
||||
if (idx_buff.tail % 2 != 0)
|
||||
return false;
|
||||
// Expect each line to be a pair next to each other
|
||||
// VS expand relies on this!
|
||||
for (u32 i = 0; i < m_index->tail; i += 2)
|
||||
for (u32 i = 0; i < idx_buff.tail; i += 2)
|
||||
{
|
||||
if (m_index->buff[i] + 1 != m_index->buff[i + 1])
|
||||
if (idx_buff.buff[i] + 1 != idx_buff.buff[i + 1])
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case GS_TRIANGLE_CLASS:
|
||||
if (m_index->tail % 3 != 0)
|
||||
if (idx_buff.tail % 3 != 0)
|
||||
return false;
|
||||
break;
|
||||
case GS_INVALID_CLASS:
|
||||
@@ -5351,15 +5361,17 @@ void GSRendererHW::HandleFlatShadedVertices()
|
||||
if (!maybe_fix_vertices || dont_fix_vertices)
|
||||
return;
|
||||
|
||||
const int n = GSUtil::GetClassVertexCount(m_vt.m_primclass);
|
||||
const u32 n = GSUtil::GetClassVertexCount(m_vt.m_primclass);
|
||||
GSVertexBuff& vtx_buff = *m_vertex;
|
||||
GSIndexBuff& idx_buff = *m_index;
|
||||
|
||||
// If all vertices of each prim have the same color there is nothing to do.
|
||||
bool prims_flat = true;
|
||||
for (u32 i = 0; i < m_index->tail; i += n)
|
||||
for (u32 i = 0; i < idx_buff.tail; i += n)
|
||||
{
|
||||
for (u32 j = 0; j < n - 1; j++)
|
||||
{
|
||||
if (m_vertex->buff[m_index->buff[i + j]].RGBAQ.U32[0] != m_vertex->buff[m_index->buff[i + n - 1]].RGBAQ.U32[0])
|
||||
if (vtx_buff.buff[idx_buff.buff[i + j]].RGBAQ.U32[0] != vtx_buff.buff[idx_buff.buff[i + n - 1]].RGBAQ.U32[0])
|
||||
{
|
||||
prims_flat = false;
|
||||
break;
|
||||
@@ -5372,21 +5384,22 @@ void GSRendererHW::HandleFlatShadedVertices()
|
||||
return;
|
||||
|
||||
// De-index the vertices using the copy buffer
|
||||
while (m_vertex->maxcount < m_index->tail)
|
||||
while (vtx_buff.maxcount < idx_buff.tail)
|
||||
GrowVertexBuffer();
|
||||
for (int i = static_cast<int>(m_index->tail) - 1; i >= 0; i--)
|
||||
|
||||
for (int i = static_cast<int>(idx_buff.tail) - 1; i >= 0; i--)
|
||||
{
|
||||
m_vertex->buff_copy[i] = m_vertex->buff[m_index->buff[i]];
|
||||
m_index->buff[i] = static_cast<u16>(i);
|
||||
vtx_buff.buff_copy[i] = vtx_buff.buff[idx_buff.buff[i]];
|
||||
idx_buff.buff[i] = static_cast<u16>(i);
|
||||
}
|
||||
std::swap(m_vertex->buff, m_vertex->buff_copy);
|
||||
m_vertex->head = m_vertex->next = m_vertex->tail = m_index->tail;
|
||||
std::swap(vtx_buff.buff, vtx_buff.buff_copy);
|
||||
vtx_buff.head = vtx_buff.next = vtx_buff.tail = idx_buff.tail;
|
||||
|
||||
// Make all vertices the same color to simplify handling in expand shaders.
|
||||
for (u32 i = 0; i < m_index->tail; i += n)
|
||||
for (u32 i = 0; i < idx_buff.tail; i += n)
|
||||
{
|
||||
for (u32 j = 0; j < n - 1; j++)
|
||||
m_vertex->buff[i + j].RGBAQ.U32[0] = m_vertex->buff[i + n - 1].RGBAQ.U32[0];
|
||||
vtx_buff.buff[i + j].RGBAQ.U32[0] = vtx_buff.buff[i + n - 1].RGBAQ.U32[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9869,11 +9882,13 @@ bool GSRendererHW::DetectStripedDoubleClear(bool& no_rt, bool& no_ds)
|
||||
// and I could cheat and stop when we get a size that matches, but that might be a lucky misdetection, I don't wanna risk it.
|
||||
int vertex_offset = 0;
|
||||
int last_vertex = m_vertex->buff[0].XYZ.X;
|
||||
const u32 tail = m_vertex->tail;
|
||||
GSVertexBuff& vtx_buff = *m_vertex;
|
||||
|
||||
for (u32 i = 1; i < m_vertex->tail; i++)
|
||||
for (u32 i = 1; i < tail; i++)
|
||||
{
|
||||
vertex_offset = std::max(static_cast<int>((m_vertex->buff[i].XYZ.X - last_vertex) >> 4), vertex_offset);
|
||||
last_vertex = m_vertex->buff[i].XYZ.X;
|
||||
vertex_offset = std::max(static_cast<int>((vtx_buff.buff[i].XYZ.X - last_vertex) >> 4), vertex_offset);
|
||||
last_vertex = vtx_buff.buff[i].XYZ.X;
|
||||
|
||||
// Found a gap which is much bigger, no point continuing to scan.
|
||||
if (vertex_offset > strip_size)
|
||||
@@ -10609,12 +10624,13 @@ bool GSRendererHW::TextureCoversWithoutGapsNotEqual()
|
||||
const int first_dpX = v[1].XYZ.X - v[0].XYZ.X;
|
||||
const int first_dtV = v[1].V - v[0].V;
|
||||
const int first_dtU = v[1].U - v[0].U;
|
||||
const u32 next_count = m_vertex->next;
|
||||
|
||||
// Horizontal Match.
|
||||
if ((first_dpX >> 4) == m_r.z)
|
||||
{
|
||||
// Borrowed from MergeSprite() modified to calculate heights.
|
||||
for (u32 i = 2; i < m_vertex->next; i += 2)
|
||||
for (u32 i = 2; i < next_count; i += 2)
|
||||
{
|
||||
const int last_tV = v[i - 1].V;
|
||||
const int dtV = v[i + 1].V - v[i].V;
|
||||
@@ -10632,7 +10648,7 @@ bool GSRendererHW::TextureCoversWithoutGapsNotEqual()
|
||||
if ((first_dpY >> 4) == m_r.w)
|
||||
{
|
||||
// Borrowed from MergeSprite().
|
||||
for (u32 i = 2; i < m_vertex->next; i += 2)
|
||||
for (u32 i = 2; i < next_count; i += 2)
|
||||
{
|
||||
const int last_tU = v[i - 1].U;
|
||||
const int this_start_U = v[i].U;
|
||||
@@ -10888,10 +10904,13 @@ void GSRendererHW::OffsetDraw(s32 fbp_offset, s32 zbp_offset, s32 xoffset, s32 y
|
||||
|
||||
const s32 fp_xoffset = xoffset << 4;
|
||||
const s32 fp_yoffset = yoffset << 4;
|
||||
for (u32 i = 0; i < m_vertex->next; i++)
|
||||
|
||||
GSVertexBuff& vtx_buff = *m_vertex;
|
||||
|
||||
for (u32 i = 0; i < vtx_buff.next; i++)
|
||||
{
|
||||
m_vertex->buff[i].XYZ.X += fp_xoffset;
|
||||
m_vertex->buff[i].XYZ.Y += fp_yoffset;
|
||||
vtx_buff.buff[i].XYZ.X += fp_xoffset;
|
||||
vtx_buff.buff[i].XYZ.Y += fp_yoffset;
|
||||
}
|
||||
|
||||
m_vt.m_min.p.x += static_cast<float>(xoffset);
|
||||
|
||||
Reference in New Issue
Block a user