faster/more compact MatchValueAndRef

Made commandprocessor GetcurrentRingReadcount inline, it was made noinline to match PGO decisions but i think PGO can make extra reg allocation decisions that make this inlining choice yield gains, whereas if we do it manually we lose a tiny bit of performance

Working on a more compact vectorized version of GetScissor to save icache on cmd processor thread
Add WriteRegisterForceinline, will probably end up amending to remove it

add ERMS path for vastcpy

Adding WriteRegistersFromMemCommonSense (name will be changed later), name is because i realized my approach with optimizing writeregisters has been backwards,  instead of handling all checks more quickly within the loop that writes the registers, we need a different loop for each range with unique handling. we also manage to hoist a lot of the logic out of the loops

Use 100ns delay for MaybeYield, noticed we often return almost immediately from the syscall so we end up wasting some cpu, instead we give up the cpu for the min waitable time (on my system, this is 0.5 ms)

Added a note about affinity mask/dynamic process affinity updates to threading_win

Add TextureFetchConstantsWritten
This commit is contained in:
chss95cs@gmail.com
2022-12-13 10:45:19 -08:00
parent 43d7fc5158
commit 82dcf3f951
9 changed files with 345 additions and 97 deletions

View File

@@ -4,7 +4,6 @@ void COMMAND_PROCESSOR::ExecuteIndirectBuffer(uint32_t ptr,
uint32_t count) XE_RESTRICT {
SCOPE_profile_cpu_f("gpu");
trace_writer_.WriteIndirectBufferStart(ptr, count * sizeof(uint32_t));
if (count != 0) {
RingBuffer old_reader = reader_;
@@ -32,10 +31,9 @@ void COMMAND_PROCESSOR::ExecuteIndirectBuffer(uint32_t ptr,
trace_writer_.WriteIndirectBufferEnd();
reader_ = old_reader;
} else {
//rare, but i've seen it happen! (and then a division by 0 occurs)
// rare, but i've seen it happen! (and then a division by 0 occurs)
return;
}
}
bool COMMAND_PROCESSOR::ExecutePacket() {
@@ -88,8 +86,9 @@ bool COMMAND_PROCESSOR::ExecutePacketType0_CountOverflow(uint32_t count) {
count * sizeof(uint32_t));
return false;
}
/*
Todo: optimize this function this one along with execute packet type III are the most frequently called functions for PM4
/*
Todo: optimize this function this one along with execute packet type III are
the most frequently called functions for PM4
*/
XE_NOINLINE
bool COMMAND_PROCESSOR::ExecutePacketType0(uint32_t packet) XE_RESTRICT {
@@ -99,7 +98,6 @@ bool COMMAND_PROCESSOR::ExecutePacketType0(uint32_t packet) XE_RESTRICT {
uint32_t count = ((packet >> 16) & 0x3FFF) + 1;
if (COMMAND_PROCESSOR::GetCurrentRingReadCount() >=
count * sizeof(uint32_t)) {
trace_writer_.WritePacketStart(uint32_t(reader_.read_ptr() - 4), 1 + count);
@@ -143,7 +141,7 @@ bool COMMAND_PROCESSOR::ExecutePacketType2(uint32_t packet) XE_RESTRICT {
trace_writer_.WritePacketEnd();
return true;
}
XE_NOINLINE
XE_FORCEINLINE
XE_NOALIAS
uint32_t COMMAND_PROCESSOR::GetCurrentRingReadCount() {
return reader_.read_count();
@@ -446,41 +444,46 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_INDIRECT_BUFFER(
return true;
}
XE_NOINLINE
/*
chrispy: this is fine to inline, as a noinline function it compiled down
to 54 bytes
*/
static bool MatchValueAndRef(uint32_t value, uint32_t ref, uint32_t wait_info) {
/*
Todo: should subtract values from each other twice with the sides inverted and then create a mask from the sign bits
then use the wait_info value in order to select the bits that correctly implement the condition
If neither subtraction has the signbit set then that means the value is equal
*/
bool matched = false;
switch (wait_info & 0x7) {
case 0x0: // Never.
matched = false;
break;
case 0x1: // Less than reference.
matched = value < ref;
break;
case 0x2: // Less than or equal to reference.
matched = value <= ref;
break;
case 0x3: // Equal to reference.
matched = value == ref;
break;
case 0x4: // Not equal to reference.
matched = value != ref;
break;
case 0x5: // Greater than or equal to reference.
matched = value >= ref;
break;
case 0x6: // Greater than reference.
matched = value > ref;
break;
case 0x7: // Always
matched = true;
break;
}
return matched;
// smaller code is generated than the #else path, although whether it is faster
// i do not know. i don't think games do an enormous number of cond_write
// though, so we have picked
// the path with the smaller codegen.
// we do technically have more instructions executed vs the switch case method,
// but we have no mispredicts and most of our instructions are 0.25/0.3
// throughput
#if 1
uint32_t value_minus_ref =
static_cast<uint32_t>(static_cast<int32_t>(value - ref) >> 31);
uint32_t ref_minus_value =
static_cast<uint32_t>(static_cast<int32_t>(ref - value) >> 31);
uint32_t eqmask = ~(value_minus_ref | ref_minus_value);
uint32_t nemask = (value_minus_ref | ref_minus_value);
uint32_t value_lt_mask = value_minus_ref;
uint32_t value_gt_mask = ref_minus_value;
uint32_t value_lte_mask = value_lt_mask | eqmask;
uint32_t value_gte_mask = value_gt_mask | eqmask;
uint32_t bits_for_selecting =
(value_lt_mask & (1 << 1)) | (value_lte_mask & (1 << 2)) |
(eqmask & (1 << 3)) | (nemask & (1 << 4)) | (value_gte_mask & (1 << 5)) |
(value_gt_mask & (1 << 6)) | (1 << 7);
return (bits_for_selecting >> (wait_info & 7)) & 1;
#else
return ((((value < ref) << 1) | ((value <= ref) << 2) |
((value == ref) << 3) | ((value != ref) << 4) |
((value >= ref) << 5) | ((value > ref) << 6) | (1 << 7)) >>
(wait_info & 7)) &
1;
#endif
}
XE_NOINLINE
bool COMMAND_PROCESSOR::ExecutePacketType3_WAIT_REG_MEM(
@@ -1128,7 +1131,7 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_VIZ_QUERY(
}
uint32_t COMMAND_PROCESSOR::ExecutePrimaryBuffer(uint32_t read_index,
uint32_t write_index) {
uint32_t write_index) {
SCOPE_profile_cpu_f("gpu");
#if XE_ENABLE_TRACE_WRITER_INSTRUMENTATION == 1
// If we have a pending trace stream open it now. That way we ensure we get