Files
Xenia-Canary/src/xenia/gpu/command_processor.h
chss95cs@gmail.com 8f7f7dc6ad fixed wine crash from use of NtSetEventPriorityBoost
add xe::clear_lowest_bit, use it in place of shift-andnot in some bit iteration code
make is_allocated_ and is_enabled_ volatile in xma_context
preallocate avpacket buffer in XMAContext::Setup, the reallocations of the buffer in ffmpeg were showing up on profiles
check is_enabled and is_allocated BEFORE locking an xmacontext. XMA worker was spending most of its time locking and unlocking contexts
Removed XeDMAC, dma:: namespace. It was a bad idea and I couldn't make it work in the end. Kept vastcpy and moved it to the memory namespace instead
Made the rest of global_critical_region's members static. They never needed an instance.
Removed ifdef'ed out code from ring_buffer.h
Added EventInfo struct to threading, added Event::Query to aid with implementing NtQueryEvent.
Removed vector from WaitMultiple, instead use a fixed array of 64 handles that we populate. WaitForMultipleObjects cannot handle more than 64 objects.
Remove XE_MSVC_OPTIMIZE_SMALL() use in x64_sequences, x64 backend is now always size optimized because of premake
Make global_critical_region_ static constexpr in shared_memory.h to get rid of wasteage of 8 bytes (empty class=1byte, +alignment for next member=8)
Move trace-related data to the tail of SharedMemory to keep more important data together
In IssueDraw build an array of fetch constant addresses/sizes, then pre-lock the global lock before doing requestrange for each instead of individually locking within requestrange for each of them
Consistent access specifier protected for pm4_command_processor_declare
Devirtualize WriteOneRegisterFromRing.
Move ExecutePacket and ExecutePrimaryBuffer to pm4_command_buffer_x
Remove many redundant header inclusions access xenia-gpu
Minor microoptimization of ExecutePacketType0

Add TextureCache::RequestTextures for batch invocation of LoadTexturesData

Add TextureCache::LoadTexturesData for reducing the number of times we release and reacquire the global lock.
Ideally you should hold the global lock for as little time as possible, but if you are constantly acquiring and releasing it you are actually more likely to have contention
Add already_locked param to ObjectTable::LookupObject to help with reducing lock acquire/release pairs
Add missing checks to XAudioRegisterRenderDriverClient_entry. this is unlikely to fix anything, it was just an easy thing to do
Add NtQueryEvent system call implementation. I don't actually know of any games that need it.
Instead of using std::vector + push_back in KeWaitForMultipleObjects and xeNtWaitForMultipleObjectsEx use a fixed size array of 64 and track the count. More than 64 objects is not permitted by the kernel. The repeated reallocations from push_back were appearing unusually high on the profiler, but were masked until now by waitformultipleobjects natural overhead
Pre-lock the global lock before looking up each handle for xeNtWaitForMultipleObjectsEx and KeWaitForMultipleObjects.
Pre-lock before looking up the signal and waiter in NtSignalAndWaitForSingleObjectEx
add missing checks to NtWaitForMultipleObjectsEx
Support pre-locking in XObject::GetNativeObject
2022-10-08 09:55:17 -07:00

305 lines
9.7 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_COMMAND_PROCESSOR_H_
#define XENIA_GPU_COMMAND_PROCESSOR_H_
#include <atomic>
#include <cstring>
#include <functional>
#include <memory>
#include <mutex>
#include <queue>
#include <string>
#include <vector>
#include "xenia/base/ring_buffer.h"
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/trace_writer.h"
#include "xenia/gpu/xenos.h"
#include "xenia/kernel/xthread.h"
#include "xenia/memory.h"
#include "xenia/ui/presenter.h"
namespace xe {
class ByteStream;
namespace gpu {
class GraphicsSystem;
class Shader;
struct SwapState {
// Lock must be held when changing data in this structure.
std::mutex mutex;
// Dimensions of the framebuffer textures. Should match window size.
uint32_t width = 0;
uint32_t height = 0;
// Current front buffer, being drawn to the screen.
uintptr_t front_buffer_texture = 0;
// Current back buffer, being updated by the CP.
uintptr_t back_buffer_texture = 0;
// Backend data
void* backend_data = nullptr;
// Whether the back buffer is dirty and a swap is pending.
bool pending = false;
};
enum class SwapMode {
kNormal,
kIgnored,
};
enum class GammaRampType {
kUnknown = 0,
kTable,
kPWL,
};
class CommandProcessor {
protected:
RingBuffer
reader_; // chrispy: instead of having ringbuffer on stack, have it near
// the start of the class so we can access it via rel8. This
// also reduces the number of params we need to pass
public:
enum class SwapPostEffect {
kNone,
kFxaa,
kFxaaExtreme,
};
CommandProcessor(GraphicsSystem* graphics_system,
kernel::KernelState* kernel_state);
virtual ~CommandProcessor();
uint32_t counter() const { return counter_; }
void increment_counter() { counter_++; }
Shader* active_vertex_shader() const { return active_vertex_shader_; }
Shader* active_pixel_shader() const { return active_pixel_shader_; }
virtual bool Initialize();
virtual void Shutdown();
void CallInThread(std::function<void()> fn);
virtual void ClearCaches();
// "Desired" is for the external thread managing the post-processing effect.
SwapPostEffect GetDesiredSwapPostEffect() const {
return swap_post_effect_desired_;
}
void SetDesiredSwapPostEffect(SwapPostEffect swap_post_effect);
// Implementations must not make assumptions that the front buffer will
// necessarily be a resolve destination - it may be a texture generated by any
// means like written to by the CPU or loaded from a file (the disclaimer
// screen right in the beginning of 4D530AA4 is not a resolved render target,
// for instance).
virtual void IssueSwap(uint32_t frontbuffer_ptr, uint32_t frontbuffer_width,
uint32_t frontbuffer_height) {}
// May be called not only from the command processor thread when the command
// processor is paused, and the termination of this function may be explicitly
// awaited.
virtual void InitializeShaderStorage(const std::filesystem::path& cache_root,
uint32_t title_id, bool blocking);
virtual void RequestFrameTrace(const std::filesystem::path& root_path);
virtual void BeginTracing(const std::filesystem::path& root_path);
virtual void EndTracing();
virtual void TracePlaybackWroteMemory(uint32_t base_ptr, uint32_t length) = 0;
void RestoreRegisters(uint32_t first_register,
const uint32_t* register_values,
uint32_t register_count, bool execute_callbacks);
void RestoreGammaRamp(
const reg::DC_LUT_30_COLOR* new_gamma_ramp_256_entry_table,
const reg::DC_LUT_PWL_DATA* new_gamma_ramp_pwl_rgb,
uint32_t new_gamma_ramp_rw_component);
virtual void RestoreEdramSnapshot(const void* snapshot) = 0;
void InitializeRingBuffer(uint32_t ptr, uint32_t size_log2);
void EnableReadPointerWriteBack(uint32_t ptr, uint32_t block_size_log2);
void UpdateWritePointer(uint32_t value);
bool is_paused() const { return paused_; }
void Pause();
void Resume();
bool Save(ByteStream* stream);
bool Restore(ByteStream* stream);
protected:
struct IndexBufferInfo {
xenos::IndexFormat format = xenos::IndexFormat::kInt16;
xenos::Endian endianness = xenos::Endian::kNone;
uint32_t count = 0;
uint32_t guest_base = 0;
size_t length = 0;
};
void WorkerThreadMain();
virtual bool SetupContext() = 0;
virtual void ShutdownContext() = 0;
// rarely needed, most register writes have no special logic here
XE_NOINLINE
void HandleSpecialRegisterWrite(uint32_t index, uint32_t value);
virtual void WriteRegister(uint32_t index, uint32_t value);
// mem has big-endian register values
XE_FORCEINLINE
virtual void WriteRegistersFromMem(uint32_t start_index, uint32_t* base,
uint32_t num_registers);
XE_FORCEINLINE
virtual void WriteRegisterRangeFromRing(xe::RingBuffer* ring, uint32_t base,
uint32_t num_registers);
XE_NOINLINE
void WriteOneRegisterFromRing(
uint32_t base,
uint32_t
num_times); // repeatedly write a value to one register, presumably a
// register with special handling for writes
void WriteALURangeFromRing(xe::RingBuffer* ring, uint32_t base,
uint32_t num_times);
void WriteFetchRangeFromRing(xe::RingBuffer* ring, uint32_t base,
uint32_t num_times);
void WriteBoolRangeFromRing(xe::RingBuffer* ring, uint32_t base,
uint32_t num_times);
void WriteLoopRangeFromRing(xe::RingBuffer* ring, uint32_t base,
uint32_t num_times);
void WriteREGISTERSRangeFromRing(xe::RingBuffer* ring, uint32_t base,
uint32_t num_times);
void WriteALURangeFromMem(uint32_t start_index, uint32_t* base,
uint32_t num_registers);
void WriteFetchRangeFromMem(uint32_t start_index, uint32_t* base,
uint32_t num_registers);
void WriteBoolRangeFromMem(uint32_t start_index, uint32_t* base,
uint32_t num_registers);
void WriteLoopRangeFromMem(uint32_t start_index, uint32_t* base,
uint32_t num_registers);
void WriteREGISTERSRangeFromMem(uint32_t start_index, uint32_t* base,
uint32_t num_registers);
const reg::DC_LUT_30_COLOR* gamma_ramp_256_entry_table() const {
return gamma_ramp_256_entry_table_;
}
const reg::DC_LUT_PWL_DATA* gamma_ramp_pwl_rgb() const {
return gamma_ramp_pwl_rgb_[0];
}
virtual void OnGammaRamp256EntryTableValueWritten() {}
virtual void OnGammaRampPWLValueWritten() {}
virtual void MakeCoherent();
virtual void PrepareForWait();
virtual void ReturnFromWait();
virtual void OnPrimaryBufferEnd() {}
#include "pm4_command_processor_declare.h"
virtual Shader* LoadShader(xenos::ShaderType shader_type,
uint32_t guest_address,
const uint32_t* host_address,
uint32_t dword_count) {
return nullptr;
}
virtual bool IssueDraw(xenos::PrimitiveType prim_type, uint32_t index_count,
IndexBufferInfo* index_buffer_info,
bool major_mode_explicit) {
return false;
}
virtual bool IssueCopy() { return false; }
// "Actual" is for the command processor thread, to be read by the
// implementations.
SwapPostEffect GetActualSwapPostEffect() const {
return swap_post_effect_actual_;
}
virtual void InitializeTrace();
Memory* memory_ = nullptr;
kernel::KernelState* kernel_state_ = nullptr;
GraphicsSystem* graphics_system_ = nullptr;
RegisterFile* XE_RESTRICT register_file_ = nullptr;
TraceWriter trace_writer_;
enum class TraceState {
kDisabled,
kStreaming,
kSingleFrame,
};
TraceState trace_state_ = TraceState::kDisabled;
std::filesystem::path trace_stream_path_;
std::filesystem::path trace_frame_path_;
std::atomic<bool> worker_running_;
kernel::object_ref<kernel::XHostThread> worker_thread_;
std::queue<std::function<void()>> pending_fns_;
// MicroEngine binary from PM4_ME_INIT
std::vector<uint32_t> me_bin_;
uint32_t counter_ = 0;
uint32_t primary_buffer_ptr_ = 0;
uint32_t primary_buffer_size_ = 0;
uint32_t read_ptr_index_ = 0;
uint32_t read_ptr_update_freq_ = 0;
uint32_t read_ptr_writeback_ptr_ = 0;
std::unique_ptr<xe::threading::Event> write_ptr_index_event_;
std::atomic<uint32_t> write_ptr_index_;
uint64_t bin_select_ = 0xFFFFFFFFull;
uint64_t bin_mask_ = 0xFFFFFFFFull;
Shader* active_vertex_shader_ = nullptr;
Shader* active_pixel_shader_ = nullptr;
bool paused_ = false;
// By default (such as for tools), post-processing is disabled.
// "Desired" is for the external thread managing the post-processing effect.
SwapPostEffect swap_post_effect_desired_ = SwapPostEffect::kNone;
SwapPostEffect swap_post_effect_actual_ = SwapPostEffect::kNone;
private:
reg::DC_LUT_30_COLOR gamma_ramp_256_entry_table_[256] = {};
reg::DC_LUT_PWL_DATA gamma_ramp_pwl_rgb_[128][3] = {};
uint32_t gamma_ramp_rw_component_ = 0;
};
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_COMMAND_PROCESSOR_H_