atomic cas use prefetchw if available

remove useless memorybarrier
remove double membarrier in wait pm4 cmd
add int64 cvar
use int64 cvar for x64 feature mask
Rework some functions that were frontend bound according to vtune placing some of their code in different noinline functions, profiling after indicating l1 cache misses decreased and perf of func increased
remove long vpinsrd dep chain code for conversion.h, instead do normal load+bswap or movbe if avail
Much faster entry table via split_map, code size could be improved though
GetResolveInfo was very large and had impact on icache, mark callees as noinline + msvc pragma optimize small
use log2 shifts instead of integer divides in memory
minor optimizations in PhysicalHeap::EnableAccessCallbacks, the majority of time in the function is spent looping, NOT calling Protect! Someone should optimize this function and rework the algo completely
remove wonky scheduling log message, it was spammy and unhelpful
lock count was unnecessary for criticalsection mutex, criticalsection is already a recursive mutex
brief notes i gotta run
This commit is contained in:
chss95cs@gmail.com
2022-09-17 04:04:53 -07:00
parent 0fd4a2533b
commit eb8154908c
35 changed files with 942 additions and 738 deletions

View File

@@ -334,7 +334,7 @@ void CommandProcessor::EnableReadPointerWriteBack(uint32_t ptr,
void CommandProcessor::UpdateWritePointer(uint32_t value) {
write_ptr_index_ = value;
write_ptr_index_event_->Set();
write_ptr_index_event_->SetBoostPriority();
}
void CommandProcessor::HandleSpecialRegisterWrite(uint32_t index,
uint32_t value) {
@@ -665,6 +665,11 @@ uint32_t CommandProcessor::ExecutePrimaryBuffer(uint32_t read_index,
reader_.set_read_offset(read_index * sizeof(uint32_t));
reader_.set_write_offset(write_index * sizeof(uint32_t));
// prefetch the wraparound range
// it likely is already in L3 cache, but in a zen system it may be another
// chiplets l3
reader_.BeginPrefetchedRead<swcache::PrefetchTag::Level2>(
GetCurrentRingReadCount());
do {
if (!ExecutePacket()) {
// This probably should be fatal - but we're going to continue anyways.

File diff suppressed because it is too large Load Diff

View File

@@ -45,7 +45,10 @@
namespace xe {
namespace gpu {
namespace d3d12 {
struct MemExportRange {
uint32_t base_address_dwords;
uint32_t size_dwords;
};
class D3D12CommandProcessor final : public CommandProcessor {
public:
#include "../pm4_command_processor_declare.h"
@@ -287,8 +290,21 @@ class D3D12CommandProcessor final : public CommandProcessor {
bool IssueDraw(xenos::PrimitiveType primitive_type, uint32_t index_count,
IndexBufferInfo* index_buffer_info,
bool major_mode_explicit) override;
XE_COLD
XE_NOINLINE
bool HandleMemexportGuestDMA(ID3D12Resource*& scratch_index_buffer,
D3D12_INDEX_BUFFER_VIEW& index_buffer_view,
uint32_t guest_index_base,
bool& retflag);
XE_NOINLINE
XE_COLD
bool GatherMemexportRangesAndMakeResident(bool& retflag);
XE_NOINLINE
XE_COLD
void HandleMemexportDrawOrdering_AndReadback();
bool IssueCopy() override;
XE_NOINLINE
bool IssueCopy_ReadbackResolvePath();
void InitializeTrace() override;
private:
@@ -363,6 +379,8 @@ class D3D12CommandProcessor final : public CommandProcessor {
};
// Gets the indices of optional root parameters. Returns the total parameter
// count.
XE_NOINLINE
XE_COLD
static uint32_t GetRootBindfulExtraParameterIndices(
const DxbcShader* vertex_shader, const DxbcShader* pixel_shader,
RootBindfulExtraParameterIndices& indices_out);
@@ -437,6 +455,18 @@ class D3D12CommandProcessor final : public CommandProcessor {
bool UpdateBindings(const D3D12Shader* vertex_shader,
const D3D12Shader* pixel_shader,
ID3D12RootSignature* root_signature);
XE_COLD
XE_NOINLINE
void UpdateBindings_UpdateRootBindful();
XE_NOINLINE
XE_COLD
bool UpdateBindings_BindfulPath(
const size_t texture_layout_uid_vertex,
const std::vector<xe::gpu::DxbcShader::TextureBinding>& textures_vertex,
const size_t texture_layout_uid_pixel,
const std::vector<xe::gpu::DxbcShader::TextureBinding>* textures_pixel,
const size_t sampler_count_vertex, const size_t sampler_count_pixel,
bool& retflag);
// Returns dword count for one element for a memexport format, or 0 if it's
// not supported by the D3D12 command processor (if it's smaller that 1 dword,
@@ -743,6 +773,9 @@ class D3D12CommandProcessor final : public CommandProcessor {
draw_util::GetViewportInfoArgs previous_viewport_info_args_;
draw_util::ViewportInfo previous_viewport_info_;
// scratch memexport data
MemExportRange memexport_ranges_[512];
uint32_t memexport_range_count_ = 0;
};
} // namespace d3d12

View File

@@ -266,22 +266,9 @@ void DeferredCommandList::Execute(ID3D12GraphicsCommandList* command_list,
void* DeferredCommandList::WriteCommand(Command command,
size_t arguments_size_bytes) {
size_t arguments_size_elements =
round_up(arguments_size_bytes, sizeof(uintmax_t), false);
//(arguments_size_bytes + sizeof(uintmax_t) - 1) / sizeof(uintmax_t);
#if 0
size_t offset = command_stream_.size();
command_stream_.resize(offset + kCommandHeaderSizeElements +
arguments_size_elements);
CommandHeader& header =
*reinterpret_cast<CommandHeader*>(command_stream_.data() + offset);
header.command = command;
header.arguments_size_elements = uint32_t(arguments_size_elements);
return command_stream_.data() + (offset + kCommandHeaderSizeElements);
#else
size_t offset = command_stream_.size();
constexpr size_t kCommandHeaderSizeBytes =
kCommandHeaderSizeElements * sizeof(uintmax_t);
@@ -290,9 +277,9 @@ void* DeferredCommandList::WriteCommand(Command command,
CommandHeader& header =
*reinterpret_cast<CommandHeader*>(command_stream_.data() + offset);
header.command = command;
header.arguments_size_elements = uint32_t(arguments_size_elements) / sizeof(uintmax_t);
header.arguments_size_elements =
uint32_t(arguments_size_elements) / sizeof(uintmax_t);
return command_stream_.data() + (offset + kCommandHeaderSizeBytes);
#endif
}
} // namespace d3d12

View File

@@ -183,7 +183,7 @@ void PipelineCache::Shutdown() {
// creating them.
if (!creation_threads_.empty()) {
{
std::lock_guard<std::mutex> lock(creation_request_lock_);
std::lock_guard<xe_mutex> lock(creation_request_lock_);
creation_threads_shutdown_from_ = 0;
}
creation_request_cond_.notify_all();
@@ -681,7 +681,7 @@ void PipelineCache::InitializeShaderStorage(
if (!creation_threads_.empty()) {
// Submit the pipeline for creation to any available thread.
{
std::lock_guard<std::mutex> lock(creation_request_lock_);
std::lock_guard<xe_mutex> lock(creation_request_lock_);
creation_queue_.push_back(new_pipeline);
}
creation_request_cond_.notify_one();
@@ -695,7 +695,7 @@ void PipelineCache::InitializeShaderStorage(
CreateQueuedPipelinesOnProcessorThread();
if (creation_threads_.size() > creation_thread_original_count) {
{
std::lock_guard<std::mutex> lock(creation_request_lock_);
std::lock_guard<xe_mutex> lock(creation_request_lock_);
creation_threads_shutdown_from_ = creation_thread_original_count;
// Assuming the queue is empty because of
// CreateQueuedPipelinesOnProcessorThread.
@@ -708,7 +708,7 @@ void PipelineCache::InitializeShaderStorage(
bool await_creation_completion_event;
{
// Cleanup so additional threads can be created later again.
std::lock_guard<std::mutex> lock(creation_request_lock_);
std::lock_guard<xe_mutex> lock(creation_request_lock_);
creation_threads_shutdown_from_ = SIZE_MAX;
// If the invocation is blocking, all the shader storage
// initialization is expected to be done before proceeding, to avoid
@@ -813,7 +813,7 @@ void PipelineCache::EndSubmission() {
// Await creation of all queued pipelines.
bool await_creation_completion_event;
{
std::lock_guard<std::mutex> lock(creation_request_lock_);
std::lock_guard<xe_mutex> lock(creation_request_lock_);
// Assuming the creation queue is already empty (because the processor
// thread also worked on creating the leftover pipelines), so only check
// if there are threads with pipelines currently being created.
@@ -834,7 +834,7 @@ bool PipelineCache::IsCreatingPipelines() {
if (creation_threads_.empty()) {
return false;
}
std::lock_guard<std::mutex> lock(creation_request_lock_);
std::lock_guard<xe_mutex> lock(creation_request_lock_);
return !creation_queue_.empty() || creation_threads_busy_ != 0;
}
@@ -1076,7 +1076,7 @@ bool PipelineCache::ConfigurePipeline(
if (!creation_threads_.empty()) {
// Submit the pipeline for creation to any available thread.
{
std::lock_guard<std::mutex> lock(creation_request_lock_);
std::lock_guard<xe_mutex> lock(creation_request_lock_);
creation_queue_.push_back(new_pipeline);
}
creation_request_cond_.notify_one();
@@ -3314,7 +3314,7 @@ void PipelineCache::CreationThread(size_t thread_index) {
// Check if need to shut down or set the completion event and dequeue the
// pipeline if there is any.
{
std::unique_lock<std::mutex> lock(creation_request_lock_);
std::unique_lock<xe_mutex> lock(creation_request_lock_);
if (thread_index >= creation_threads_shutdown_from_ ||
creation_queue_.empty()) {
if (creation_completion_set_event_ && creation_threads_busy_ == 0) {
@@ -3345,7 +3345,7 @@ void PipelineCache::CreationThread(size_t thread_index) {
// completion event if needed (at the next iteration, or in some other
// thread).
{
std::lock_guard<std::mutex> lock(creation_request_lock_);
std::lock_guard<xe_mutex> lock(creation_request_lock_);
--creation_threads_busy_;
}
}
@@ -3356,7 +3356,7 @@ void PipelineCache::CreateQueuedPipelinesOnProcessorThread() {
while (true) {
Pipeline* pipeline_to_create;
{
std::lock_guard<std::mutex> lock(creation_request_lock_);
std::lock_guard<xe_mutex> lock(creation_request_lock_);
if (creation_queue_.empty()) {
break;
}

View File

@@ -403,8 +403,8 @@ class PipelineCache {
// Pipeline creation threads.
void CreationThread(size_t thread_index);
void CreateQueuedPipelinesOnProcessorThread();
std::mutex creation_request_lock_;
std::condition_variable creation_request_cond_;
xe_mutex creation_request_lock_;
std::condition_variable_any creation_request_cond_;
// Protected with creation_request_lock_, notify_one creation_request_cond_
// when set.
std::deque<Pipeline*> creation_queue_;

View File

@@ -650,7 +650,8 @@ uint32_t GetNormalizedColorMask(const RegisterFile& regs,
}
return normalized_color_mask;
}
XE_NOINLINE
XE_NOALIAS
xenos::CopySampleSelect SanitizeCopySampleSelect(
xenos::CopySampleSelect copy_sample_select, xenos::MsaaSamples msaa_samples,
bool is_depth) {
@@ -737,7 +738,7 @@ const ResolveCopyShaderInfo
{"Resolve Copy Full 64bpp", true, 2, 4, 5, 3},
{"Resolve Copy Full 128bpp", true, 2, 4, 4, 3},
};
XE_MSVC_OPTIMIZE_SMALL()
bool GetResolveInfo(const RegisterFile& regs, const Memory& memory,
TraceWriter& trace_writer, uint32_t draw_resolution_scale_x,
uint32_t draw_resolution_scale_y,
@@ -869,7 +870,8 @@ bool GetResolveInfo(const RegisterFile& regs, const Memory& memory,
y1 = y0 + int32_t(xenos::kMaxResolveSize);
}
// fails in forza horizon 1
assert_true(x0 < x1 && y0 < y1);
//x0 is 0, x1 is 0x100, y0 is 0x100, y1 is 0x100
assert_true(x0 <= x1 && y0 <= y1);
if (x0 >= x1 || y0 >= y1) {
XELOGE("Resolve region is empty");
return false;
@@ -1108,7 +1110,7 @@ bool GetResolveInfo(const RegisterFile& regs, const Memory& memory,
info_out.rb_depth_clear = regs[XE_GPU_REG_RB_DEPTH_CLEAR].u32;
info_out.rb_color_clear = regs[XE_GPU_REG_RB_COLOR_CLEAR].u32;
info_out.rb_color_clear_lo = regs[XE_GPU_REG_RB_COLOR_CLEAR_LO].u32;
#if 0
XELOGD(
"Resolve: {},{} <= x,y < {},{}, {} -> {} at 0x{:08X} (potentially "
"modified memory range 0x{:08X} to 0x{:08X})",
@@ -1119,10 +1121,10 @@ bool GetResolveInfo(const RegisterFile& regs, const Memory& memory,
xenos::ColorRenderTargetFormat(color_edram_info.format)),
FormatInfo::GetName(dest_format), rb_copy_dest_base, copy_dest_extent_start,
copy_dest_extent_end);
#endif
return true;
}
XE_MSVC_OPTIMIZE_REVERT()
ResolveCopyShaderIndex ResolveInfo::GetCopyShader(
uint32_t draw_resolution_scale_x, uint32_t draw_resolution_scale_y,
ResolveCopyShaderConstants& constants_out, uint32_t& group_count_x_out,

View File

@@ -475,6 +475,8 @@ inline uint32_t GetD3D10SampleIndexForGuest2xMSAA(
// To avoid passing values that the shader won't understand (even though
// Direct3D 9 shouldn't pass them anyway).
XE_NOINLINE
XE_NOALIAS
xenos::CopySampleSelect SanitizeCopySampleSelect(
xenos::CopySampleSelect copy_sample_select, xenos::MsaaSamples msaa_samples,
bool is_depth);

View File

@@ -14,6 +14,11 @@ void COMMAND_PROCESSOR::ExecuteIndirectBuffer(uint32_t ptr,
new (&reader_)
RingBuffer(memory_->TranslatePhysical(ptr), count * sizeof(uint32_t));
reader_.set_write_offset(count * sizeof(uint32_t));
// prefetch the wraparound range
// it likely is already in L3 cache, but in a zen system it may be another
// chiplets l3
reader_.BeginPrefetchedRead<swcache::PrefetchTag::Level2>(
COMMAND_PROCESSOR::GetCurrentRingReadCount());
do {
if (COMMAND_PROCESSOR::ExecutePacket()) {
continue;
@@ -30,11 +35,6 @@ void COMMAND_PROCESSOR::ExecuteIndirectBuffer(uint32_t ptr,
}
bool COMMAND_PROCESSOR::ExecutePacket() {
// prefetch the wraparound range
// it likely is already in L3 cache, but in a zen system it may be another
// chiplets l3
reader_.BeginPrefetchedRead<swcache::PrefetchTag::Level2>(
COMMAND_PROCESSOR::GetCurrentRingReadCount());
const uint32_t packet = reader_.ReadAndSwap<uint32_t>();
const uint32_t packet_type = packet >> 30;
@@ -495,7 +495,7 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_WAIT_REG_MEM(
} else {
xe::threading::Sleep(std::chrono::milliseconds(wait / 0x100));
}
xe::threading::SyncMemory();
// xe::threading::SyncMemory();
ReturnFromWait();
if (!worker_running_) {
@@ -599,27 +599,28 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_COND_WRITE(
value = register_file_->values[poll_reg_addr].u32;
}
bool matched = false;
value &= mask;
switch (wait_info & 0x7) {
case 0x0: // Never.
matched = false;
break;
case 0x1: // Less than reference.
matched = (value & mask) < ref;
matched = value < ref;
break;
case 0x2: // Less than or equal to reference.
matched = (value & mask) <= ref;
matched = value <= ref;
break;
case 0x3: // Equal to reference.
matched = (value & mask) == ref;
matched = value == ref;
break;
case 0x4: // Not equal to reference.
matched = (value & mask) != ref;
matched = value != ref;
break;
case 0x5: // Greater than or equal to reference.
matched = (value & mask) >= ref;
matched = value >= ref;
break;
case 0x6: // Greater than reference.
matched = (value & mask) > ref;
matched = value > ref;
break;
case 0x7: // Always
matched = true;
@@ -1064,7 +1065,7 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_IM_LOAD_IMMEDIATE(
assert_true(count - 2 >= size_dwords);
auto shader = COMMAND_PROCESSOR::LoadShader(
shader_type, uint32_t(reader_.read_ptr()),
reinterpret_cast<uint32_t*>(reader_.read_ptr()), size_dwords);
reinterpret_cast<uint32_t*>(reader_.read_ptr()), size_dwords);
switch (shader_type) {
case xenos::ShaderType::kVertex:
active_vertex_shader_ = shader;

View File

@@ -430,7 +430,7 @@ class PrimitiveProcessor {
--count;
uint32_t index = *(source++) & low_bits_mask_guest_endian;
*(dest++) = index != reset_index_guest_endian
? xenos::GpuSwap(index, HostSwap)
? xenos::GpuSwapInline(index, HostSwap)
: UINT32_MAX;
}
if (count >= kSimdVectorU32Elements) {
@@ -442,10 +442,10 @@ class PrimitiveProcessor {
__m128i host_swap_shuffle;
if constexpr (HostSwap != xenos::Endian::kNone) {
host_swap_shuffle = _mm_set_epi32(
int32_t(xenos::GpuSwap(uint32_t(0x0F0E0D0C), HostSwap)),
int32_t(xenos::GpuSwap(uint32_t(0x0B0A0908), HostSwap)),
int32_t(xenos::GpuSwap(uint32_t(0x07060504), HostSwap)),
int32_t(xenos::GpuSwap(uint32_t(0x03020100), HostSwap)));
int32_t(xenos::GpuSwapInline(uint32_t(0x0F0E0D0C), HostSwap)),
int32_t(xenos::GpuSwapInline(uint32_t(0x0B0A0908), HostSwap)),
int32_t(xenos::GpuSwapInline(uint32_t(0x07060504), HostSwap)),
int32_t(xenos::GpuSwapInline(uint32_t(0x03020100), HostSwap)));
}
#endif // XE_ARCH_AMD64
while (count >= kSimdVectorU32Elements) {
@@ -490,7 +490,7 @@ class PrimitiveProcessor {
while (count--) {
uint32_t index = *(source++) & low_bits_mask_guest_endian;
*(dest++) = index != reset_index_guest_endian
? xenos::GpuSwap(index, HostSwap)
? xenos::GpuSwapInline(index, HostSwap)
: UINT32_MAX;
}
}
@@ -510,19 +510,19 @@ class PrimitiveProcessor {
};
struct To24Swapping8In16IndexTransform {
uint32_t operator()(uint32_t index) const {
return xenos::GpuSwap(index, xenos::Endian::k8in16) &
return xenos::GpuSwapInline(index, xenos::Endian::k8in16) &
xenos::kVertexIndexMask;
}
};
struct To24Swapping8In32IndexTransform {
uint32_t operator()(uint32_t index) const {
return xenos::GpuSwap(index, xenos::Endian::k8in32) &
return xenos::GpuSwapInline(index, xenos::Endian::k8in32) &
xenos::kVertexIndexMask;
}
};
struct To24Swapping16In32IndexTransform {
uint32_t operator()(uint32_t index) const {
return xenos::GpuSwap(index, xenos::Endian::k16in32) &
return xenos::GpuSwapInline(index, xenos::Endian::k16in32) &
xenos::kVertexIndexMask;
}
};

View File

@@ -388,6 +388,7 @@ bool SharedMemory::RequestRange(uint32_t start, uint32_t length,
bool any_data_resolved = false;
uint32_t block_first = page_first >> 6;
swcache::PrefetchL1(&system_page_flags_[block_first]);
uint32_t block_last = page_last >> 6;
uint32_t range_start = UINT32_MAX;

View File

@@ -464,7 +464,8 @@ TextureGuestLayout GetGuestTextureLayout(
return layout;
}
XE_NOINLINE
XE_NOALIAS
int32_t GetTiledOffset2D(int32_t x, int32_t y, uint32_t pitch,
uint32_t bytes_per_block_log2) {
// https://github.com/gildor2/UModel/blob/de8fbd3bc922427ea056b7340202dcdcc19ccff5/Unreal/UnTexture.cpp#L489
@@ -481,7 +482,8 @@ int32_t GetTiledOffset2D(int32_t x, int32_t y, uint32_t pitch,
return ((offset & ~0x1FF) << 3) + ((y & 16) << 7) + ((offset & 0x1C0) << 2) +
(((((y & 8) >> 2) + (x >> 3)) & 3) << 6) + (offset & 0x3F);
}
XE_NOINLINE
XE_NOALIAS
int32_t GetTiledOffset3D(int32_t x, int32_t y, int32_t z, uint32_t pitch,
uint32_t height, uint32_t bytes_per_block_log2) {
// Reconstructed from disassembly of XGRAPHICS::TileVolume.
@@ -509,7 +511,8 @@ int32_t GetTiledOffset3D(int32_t x, int32_t y, int32_t z, uint32_t pitch,
address += offset2 & 63;
return address;
}
XE_NOINLINE
XE_NOALIAS
uint32_t GetTiledAddressUpperBound2D(uint32_t right, uint32_t bottom,
uint32_t pitch,
uint32_t bytes_per_block_log2) {
@@ -538,7 +541,8 @@ uint32_t GetTiledAddressUpperBound2D(uint32_t right, uint32_t bottom,
}
return upper_bound;
}
XE_NOINLINE
XE_NOALIAS
uint32_t GetTiledAddressUpperBound3D(uint32_t right, uint32_t bottom,
uint32_t back, uint32_t pitch,
uint32_t height,

View File

@@ -280,8 +280,12 @@ void GetTextureTotalSize(xenos::DataDimension dimension,
// bytes_per_block_log2 is log2_floor according to how Direct3D 9 calculates it,
// but k_32_32_32 textures are never tiled anyway likely.
XE_NOINLINE
XE_NOALIAS
int32_t GetTiledOffset2D(int32_t x, int32_t y, uint32_t pitch,
uint32_t bytes_per_block_log2);
XE_NOINLINE
XE_NOALIAS
int32_t GetTiledOffset3D(int32_t x, int32_t y, int32_t z, uint32_t pitch,
uint32_t height, uint32_t bytes_per_block_log2);
// Because (0, 0, 0) within each 32x32x4-block tile is stored in memory first,
@@ -308,9 +312,13 @@ inline uint32_t GetTiledAddressLowerBound3D(uint32_t left, uint32_t top,
// Supporting the right > pitch and bottom > height (in tiles) cases also, for
// estimation how far addresses can actually go even potentially beyond the
// subresource stride.
XE_NOINLINE
XE_NOALIAS
uint32_t GetTiledAddressUpperBound2D(uint32_t right, uint32_t bottom,
uint32_t pitch,
uint32_t bytes_per_block_log2);
XE_NOINLINE
XE_NOALIAS
uint32_t GetTiledAddressUpperBound3D(uint32_t right, uint32_t bottom,
uint32_t back, uint32_t pitch,
uint32_t height,

View File

@@ -125,8 +125,8 @@ float Float7e3To32(uint32_t f10) {
// Based on CFloat24 from d3dref9.dll and the 6e4 code from:
// https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp
// 6e4 has a different exponent bias allowing [0,512) values, 20e4 allows [0,2).
uint32_t Float32To20e4(float f32, bool round_to_nearest_even) {
XE_NOALIAS
uint32_t Float32To20e4(float f32, bool round_to_nearest_even) noexcept {
if (!(f32 > 0.0f)) {
// Positive only, and not -0 or NaN.
return 0;
@@ -150,8 +150,8 @@ uint32_t Float32To20e4(float f32, bool round_to_nearest_even) {
}
return (f32u32 >> 3) & 0xFFFFFF;
}
float Float20e4To32(uint32_t f24) {
XE_NOALIAS
float Float20e4To32(uint32_t f24) noexcept {
f24 &= 0xFFFFFF;
if (!f24) {
return 0.0f;

View File

@@ -421,10 +421,12 @@ float Float7e3To32(uint32_t f10);
// floating-point number.
// Converts an IEEE-754 32-bit floating-point number to Xenos floating-point
// depth, rounding to the nearest even or towards zero.
uint32_t Float32To20e4(float f32, bool round_to_nearest_even);
XE_NOALIAS
uint32_t Float32To20e4(float f32, bool round_to_nearest_even) noexcept;
// Converts Xenos floating-point depth in bits 0:23 (not clamping) to an
// IEEE-754 32-bit floating-point number.
float Float20e4To32(uint32_t f24);
XE_NOALIAS
float Float20e4To32(uint32_t f24) noexcept;
// Converts 24-bit unorm depth in the value (not clamping) to an IEEE-754 32-bit
// floating-point number.
constexpr float UNorm24To32(uint32_t n24) {
@@ -1045,9 +1047,9 @@ inline uint16_t GpuSwap(uint16_t value, Endian endianness) {
return value;
}
}
XE_NOINLINE
XE_FORCEINLINE
XE_NOALIAS
static uint32_t GpuSwap(uint32_t value, Endian endianness) {
static uint32_t GpuSwapInline(uint32_t value, Endian endianness) {
switch (endianness) {
default:
case Endian::kNone:
@@ -1065,6 +1067,11 @@ static uint32_t GpuSwap(uint32_t value, Endian endianness) {
return ((value >> 16) & 0xFFFF) | (value << 16);
}
}
XE_NOINLINE
XE_NOALIAS
static uint32_t GpuSwap(uint32_t value, Endian endianness) {
return GpuSwapInline(value, endianness);
}
inline float GpuSwap(float value, Endian endianness) {
union {