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
This commit is contained in:
@@ -9,23 +9,16 @@
|
||||
|
||||
#include "xenia/gpu/command_processor.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
#include "xenia/base/byte_stream.h"
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/base/ring_buffer.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
#include "xenia/gpu/graphics_system.h"
|
||||
#include "xenia/gpu/sampler_info.h"
|
||||
#include "xenia/gpu/texture_info.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/user_module.h"
|
||||
|
||||
@@ -46,11 +39,6 @@ CommandProcessor::CommandProcessor(GraphicsSystem* graphics_system,
|
||||
write_ptr_index_event_(xe::threading::Event::CreateAutoResetEvent(false)),
|
||||
write_ptr_index_(0) {
|
||||
assert_not_null(write_ptr_index_event_);
|
||||
#if 0
|
||||
dmac_ = dma::CreateDMAC();
|
||||
#else
|
||||
dmac_ = nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
CommandProcessor::~CommandProcessor() = default;
|
||||
@@ -625,78 +613,6 @@ void CommandProcessor::PrepareForWait() { trace_writer_.Flush(); }
|
||||
|
||||
void CommandProcessor::ReturnFromWait() {}
|
||||
|
||||
uint32_t CommandProcessor::ExecutePrimaryBuffer(uint32_t read_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
|
||||
// all commands.
|
||||
if (!trace_writer_.is_open() && trace_state_ == TraceState::kStreaming) {
|
||||
uint32_t title_id = kernel_state_->GetExecutableModule()
|
||||
? kernel_state_->GetExecutableModule()->title_id()
|
||||
: 0;
|
||||
auto file_name = fmt::format("{:08X}_stream.xtr", title_id);
|
||||
auto path = trace_stream_path_ / file_name;
|
||||
trace_writer_.Open(path, title_id);
|
||||
InitializeTrace();
|
||||
}
|
||||
#endif
|
||||
// Adjust pointer base.
|
||||
uint32_t start_ptr = primary_buffer_ptr_ + read_index * sizeof(uint32_t);
|
||||
start_ptr = (primary_buffer_ptr_ & ~0x1FFFFFFF) | (start_ptr & 0x1FFFFFFF);
|
||||
uint32_t end_ptr = primary_buffer_ptr_ + write_index * sizeof(uint32_t);
|
||||
end_ptr = (primary_buffer_ptr_ & ~0x1FFFFFFF) | (end_ptr & 0x1FFFFFFF);
|
||||
|
||||
trace_writer_.WritePrimaryBufferStart(start_ptr, write_index - read_index);
|
||||
|
||||
// Execute commands!
|
||||
|
||||
RingBuffer old_reader = reader_;
|
||||
new (&reader_) RingBuffer(memory_->TranslatePhysical(primary_buffer_ptr_),
|
||||
primary_buffer_size_);
|
||||
|
||||
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.
|
||||
XELOGE("**** PRIMARY RINGBUFFER: Failed to execute packet.");
|
||||
assert_always();
|
||||
break;
|
||||
}
|
||||
} while (reader_.read_count());
|
||||
|
||||
OnPrimaryBufferEnd();
|
||||
|
||||
trace_writer_.WritePrimaryBufferEnd();
|
||||
|
||||
reader_ = old_reader;
|
||||
return write_index;
|
||||
}
|
||||
|
||||
void CommandProcessor::ExecutePacket(uint32_t ptr, uint32_t count) {
|
||||
// Execute commands!
|
||||
RingBuffer old_reader = reader_;
|
||||
|
||||
new (&reader_)
|
||||
RingBuffer{memory_->TranslatePhysical(ptr), count * sizeof(uint32_t)};
|
||||
|
||||
reader_.set_write_offset(count * sizeof(uint32_t));
|
||||
|
||||
do {
|
||||
if (!ExecutePacket()) {
|
||||
XELOGE("**** ExecutePacket: Failed to execute packet.");
|
||||
assert_always();
|
||||
break;
|
||||
}
|
||||
} while (reader_.read_count());
|
||||
reader_ = old_reader;
|
||||
}
|
||||
|
||||
void CommandProcessor::InitializeTrace() {
|
||||
// Write the initial register values, to be loaded directly into the
|
||||
|
||||
@@ -19,11 +19,8 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/dma.h"
|
||||
#include "xenia/base/ring_buffer.h"
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/gpu/register_file.h"
|
||||
#include "xenia/gpu/registers.h"
|
||||
#include "xenia/gpu/trace_writer.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/kernel/xthread.h"
|
||||
@@ -82,7 +79,6 @@ class CommandProcessor {
|
||||
CommandProcessor(GraphicsSystem* graphics_system,
|
||||
kernel::KernelState* kernel_state);
|
||||
virtual ~CommandProcessor();
|
||||
dma::XeDMAC* GetDMAC() const { return dmac_; }
|
||||
uint32_t counter() const { return counter_; }
|
||||
void increment_counter() { counter_++; }
|
||||
|
||||
@@ -135,7 +131,7 @@ class CommandProcessor {
|
||||
|
||||
void UpdateWritePointer(uint32_t value);
|
||||
|
||||
void ExecutePacket(uint32_t ptr, uint32_t count);
|
||||
|
||||
|
||||
bool is_paused() const { return paused_; }
|
||||
void Pause();
|
||||
@@ -172,7 +168,7 @@ class CommandProcessor {
|
||||
uint32_t num_registers);
|
||||
|
||||
XE_NOINLINE
|
||||
virtual void WriteOneRegisterFromRing(
|
||||
void WriteOneRegisterFromRing(
|
||||
uint32_t base,
|
||||
uint32_t
|
||||
num_times); // repeatedly write a value to one register, presumably a
|
||||
@@ -221,7 +217,7 @@ class CommandProcessor {
|
||||
virtual void PrepareForWait();
|
||||
virtual void ReturnFromWait();
|
||||
|
||||
uint32_t ExecutePrimaryBuffer(uint32_t start_index, uint32_t end_index);
|
||||
|
||||
virtual void OnPrimaryBufferEnd() {}
|
||||
|
||||
#include "pm4_command_processor_declare.h"
|
||||
@@ -300,7 +296,6 @@ class CommandProcessor {
|
||||
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;
|
||||
dma::XeDMAC* dmac_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
@@ -2672,45 +2672,66 @@ bool D3D12CommandProcessor::IssueDraw(xenos::PrimitiveType primitive_type,
|
||||
// validity is tracked.
|
||||
const Shader::ConstantRegisterMap& constant_map_vertex =
|
||||
vertex_shader->constant_register_map();
|
||||
for (uint32_t i = 0; i < xe::countof(constant_map_vertex.vertex_fetch_bitmap);
|
||||
++i) {
|
||||
uint32_t vfetch_bits_remaining = constant_map_vertex.vertex_fetch_bitmap[i];
|
||||
uint32_t j;
|
||||
while (xe::bit_scan_forward(vfetch_bits_remaining, &j)) {
|
||||
vfetch_bits_remaining &= ~(uint32_t(1) << j);
|
||||
uint32_t vfetch_index = i * 32 + j;
|
||||
const auto& vfetch_constant = regs.Get<xenos::xe_gpu_vertex_fetch_t>(
|
||||
XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + vfetch_index * 2);
|
||||
switch (vfetch_constant.type) {
|
||||
case xenos::FetchConstantType::kVertex:
|
||||
break;
|
||||
case xenos::FetchConstantType::kInvalidVertex:
|
||||
if (cvars::gpu_allow_invalid_fetch_constants) {
|
||||
{
|
||||
uint32_t vfetch_addresses[96];
|
||||
uint32_t vfetch_sizes[96];
|
||||
uint32_t vfetch_current_queued = 0;
|
||||
for (uint32_t i = 0;
|
||||
i < xe::countof(constant_map_vertex.vertex_fetch_bitmap); ++i) {
|
||||
uint32_t vfetch_bits_remaining =
|
||||
constant_map_vertex.vertex_fetch_bitmap[i];
|
||||
uint32_t j;
|
||||
while (xe::bit_scan_forward(vfetch_bits_remaining, &j)) {
|
||||
vfetch_bits_remaining = xe::clear_lowest_bit(vfetch_bits_remaining);
|
||||
uint32_t vfetch_index = i * 32 + j;
|
||||
const auto& vfetch_constant = regs.Get<xenos::xe_gpu_vertex_fetch_t>(
|
||||
XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + vfetch_index * 2);
|
||||
switch (vfetch_constant.type) {
|
||||
case xenos::FetchConstantType::kVertex:
|
||||
break;
|
||||
}
|
||||
XELOGW(
|
||||
"Vertex fetch constant {} ({:08X} {:08X}) has \"invalid\" type! "
|
||||
"This is incorrect behavior, but you can try bypassing this by "
|
||||
"launching Xenia with --gpu_allow_invalid_fetch_constants=true.",
|
||||
vfetch_index, vfetch_constant.dword_0, vfetch_constant.dword_1);
|
||||
return false;
|
||||
default:
|
||||
XELOGW(
|
||||
"Vertex fetch constant {} ({:08X} {:08X}) is completely invalid!",
|
||||
vfetch_index, vfetch_constant.dword_0, vfetch_constant.dword_1);
|
||||
return false;
|
||||
case xenos::FetchConstantType::kInvalidVertex:
|
||||
if (cvars::gpu_allow_invalid_fetch_constants) {
|
||||
break;
|
||||
}
|
||||
XELOGW(
|
||||
"Vertex fetch constant {} ({:08X} {:08X}) has \"invalid\" "
|
||||
"type! "
|
||||
"This is incorrect behavior, but you can try bypassing this by "
|
||||
"launching Xenia with "
|
||||
"--gpu_allow_invalid_fetch_constants=true.",
|
||||
vfetch_index, vfetch_constant.dword_0, vfetch_constant.dword_1);
|
||||
return false;
|
||||
default:
|
||||
XELOGW(
|
||||
"Vertex fetch constant {} ({:08X} {:08X}) is completely "
|
||||
"invalid!",
|
||||
vfetch_index, vfetch_constant.dword_0, vfetch_constant.dword_1);
|
||||
return false;
|
||||
}
|
||||
vfetch_addresses[vfetch_current_queued] = vfetch_constant.address;
|
||||
vfetch_sizes[vfetch_current_queued++] = vfetch_constant.size;
|
||||
}
|
||||
if (!shared_memory_->RequestRange(vfetch_constant.address << 2,
|
||||
vfetch_constant.size << 2)) {
|
||||
XELOGE(
|
||||
"Failed to request vertex buffer at 0x{:08X} (size {}) in the "
|
||||
"shared memory",
|
||||
vfetch_constant.address << 2, vfetch_constant.size << 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vfetch_current_queued) {
|
||||
// so far, i have never seen vfetch_current_queued > 4. 1 is most common, 2 happens occasionally. did not test many games though
|
||||
// pre-acquire the critical region so we're not repeatedly re-acquiring it
|
||||
// in requestrange
|
||||
auto shared_memory_request_range_hoisted =
|
||||
global_critical_region::Acquire();
|
||||
|
||||
for (uint32_t i = 0; i < vfetch_current_queued; ++i) {
|
||||
if (!shared_memory_->RequestRange(vfetch_addresses[i] << 2,
|
||||
vfetch_sizes[i] << 2)) {
|
||||
XELOGE(
|
||||
"Failed to request vertex buffer at 0x{:08X} (size {}) in the "
|
||||
"shared memory",
|
||||
vfetch_addresses[i] << 2, vfetch_sizes[i] << 2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Gather memexport ranges and ensure the heaps for them are resident, and
|
||||
// also load the data surrounding the export and to fill the regions that
|
||||
// won't be modified by the shaders.
|
||||
@@ -3076,24 +3097,6 @@ void D3D12CommandProcessor::InitializeTrace() {
|
||||
shared_memory_->InitializeTraceCompleteDownloads();
|
||||
}
|
||||
}
|
||||
static void DmaPrefunc(dma::XeDMAJob* job) {
|
||||
D3D12_RANGE readback_range;
|
||||
readback_range.Begin = 0;
|
||||
readback_range.End = job->size;
|
||||
void* readback_mapping;
|
||||
ID3D12Resource* readback_buffer = (ID3D12Resource*)job->userdata1;
|
||||
|
||||
HRESULT mapres = readback_buffer->Map(0, &readback_range, &readback_mapping);
|
||||
xenia_assert(SUCCEEDED(mapres));
|
||||
|
||||
job->source = (uint8_t*)readback_mapping;
|
||||
}
|
||||
|
||||
static void DmaPostfunc(dma::XeDMAJob* job) {
|
||||
D3D12_RANGE readback_write_range = {};
|
||||
ID3D12Resource* readback_buffer = (ID3D12Resource*)job->userdata1;
|
||||
readback_buffer->Unmap(0, &readback_write_range);
|
||||
}
|
||||
|
||||
bool D3D12CommandProcessor::IssueCopy() {
|
||||
#if XE_UI_D3D12_FINE_GRAINED_DRAW_SCOPES
|
||||
@@ -3102,7 +3105,6 @@ bool D3D12CommandProcessor::IssueCopy() {
|
||||
if (!BeginSubmission(true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!cvars::d3d12_readback_resolve) {
|
||||
uint32_t written_address, written_length;
|
||||
return render_target_cache_->Resolve(*memory_, *shared_memory_,
|
||||
@@ -3129,34 +3131,21 @@ bool D3D12CommandProcessor::IssueCopy_ReadbackResolvePath() {
|
||||
readback_buffer, 0, shared_memory_buffer, written_address,
|
||||
written_length);
|
||||
if (AwaitAllQueueOperationsCompletion()) {
|
||||
#if 1
|
||||
D3D12_RANGE readback_range;
|
||||
readback_range.Begin = 0;
|
||||
readback_range.End = written_length;
|
||||
void* readback_mapping;
|
||||
if (SUCCEEDED(
|
||||
readback_buffer->Map(0, &readback_range, &readback_mapping))) {
|
||||
// chrispy: this memcpy needs to be optimized as much as possible
|
||||
D3D12_RANGE readback_range;
|
||||
readback_range.Begin = 0;
|
||||
readback_range.End = written_length;
|
||||
void* readback_mapping;
|
||||
if (SUCCEEDED(readback_buffer->Map(0, &readback_range,
|
||||
&readback_mapping))) {
|
||||
// chrispy: this memcpy needs to be optimized as much as possible
|
||||
|
||||
auto physaddr = memory_->TranslatePhysical(written_address);
|
||||
dma::vastcpy(physaddr, (uint8_t*)readback_mapping, written_length);
|
||||
// XEDmaCpy(physaddr, readback_mapping, written_length);
|
||||
D3D12_RANGE readback_write_range = {};
|
||||
readback_buffer->Unmap(0, &readback_write_range);
|
||||
}
|
||||
|
||||
#else
|
||||
dma::XeDMAJob job{};
|
||||
job.destination = memory_->TranslatePhysical(written_address);
|
||||
job.size = written_length;
|
||||
job.source = nullptr;
|
||||
job.userdata1 = (void*)readback_buffer;
|
||||
job.precall = DmaPrefunc;
|
||||
job.postcall = DmaPostfunc;
|
||||
|
||||
readback_available_ = GetDMAC()->PushDMAJob(&job);
|
||||
|
||||
#endif
|
||||
auto physaddr = memory_->TranslatePhysical(written_address);
|
||||
memory::vastcpy(physaddr, (uint8_t*)readback_mapping,
|
||||
written_length);
|
||||
// XEDmaCpy(physaddr, readback_mapping, written_length);
|
||||
D3D12_RANGE readback_write_range = {};
|
||||
readback_buffer->Unmap(0, &readback_write_range);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3833,7 +3822,8 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
|
||||
uint32_t user_clip_plane_index;
|
||||
while (xe::bit_scan_forward(user_clip_planes_remaining,
|
||||
&user_clip_plane_index)) {
|
||||
user_clip_planes_remaining &= ~(UINT32_C(1) << user_clip_plane_index);
|
||||
user_clip_planes_remaining =
|
||||
xe::clear_lowest_bit(user_clip_planes_remaining);
|
||||
const float* user_clip_plane =
|
||||
®s[XE_GPU_REG_PA_CL_UCP_0_X + user_clip_plane_index * 4].f32;
|
||||
if (std::memcmp(user_clip_plane_write_ptr, user_clip_plane,
|
||||
@@ -3917,7 +3907,7 @@ XE_NOINLINE void D3D12CommandProcessor::UpdateSystemConstantValues_Impl(
|
||||
uint32_t textures_remaining = used_texture_mask;
|
||||
uint32_t texture_index;
|
||||
while (xe::bit_scan_forward(textures_remaining, &texture_index)) {
|
||||
textures_remaining &= ~(uint32_t(1) << texture_index);
|
||||
textures_remaining = xe::clear_lowest_bit(textures_remaining);
|
||||
uint32_t& texture_signs_uint =
|
||||
system_constants_.texture_swizzled_signs[texture_index >> 2];
|
||||
uint32_t texture_signs_shift = (texture_index & 3) * 8;
|
||||
@@ -5116,12 +5106,6 @@ ID3D12Resource* D3D12CommandProcessor::RequestReadbackBuffer(uint32_t size) {
|
||||
if (size == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
#if 1
|
||||
if (readback_available_) {
|
||||
GetDMAC()->WaitJobDone(readback_available_);
|
||||
readback_available_ = 0;
|
||||
}
|
||||
#endif
|
||||
size = xe::align(size, kReadbackBufferSizeIncrement);
|
||||
if (size > readback_buffer_size_) {
|
||||
const ui::d3d12::D3D12Provider& provider = GetD3D12Provider();
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <utility>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/dma.h"
|
||||
#include "xenia/gpu/command_processor.h"
|
||||
#include "xenia/gpu/d3d12/d3d12_graphics_system.h"
|
||||
#include "xenia/gpu/d3d12/d3d12_primitive_processor.h"
|
||||
@@ -50,8 +49,10 @@ struct MemExportRange {
|
||||
uint32_t size_dwords;
|
||||
};
|
||||
class D3D12CommandProcessor final : public CommandProcessor {
|
||||
public:
|
||||
protected:
|
||||
#include "../pm4_command_processor_declare.h"
|
||||
|
||||
public:
|
||||
explicit D3D12CommandProcessor(D3D12GraphicsSystem* graphics_system,
|
||||
kernel::KernelState* kernel_state);
|
||||
~D3D12CommandProcessor();
|
||||
@@ -232,8 +233,8 @@ class D3D12CommandProcessor final : public CommandProcessor {
|
||||
uint32_t base,
|
||||
uint32_t num_registers);
|
||||
XE_NOINLINE
|
||||
virtual void WriteOneRegisterFromRing(uint32_t base,
|
||||
uint32_t num_times) override;
|
||||
void WriteOneRegisterFromRing(uint32_t base,
|
||||
uint32_t num_times);
|
||||
|
||||
XE_FORCEINLINE
|
||||
void WriteALURangeFromRing(xe::RingBuffer* ring, uint32_t base,
|
||||
@@ -677,7 +678,6 @@ class D3D12CommandProcessor final : public CommandProcessor {
|
||||
|
||||
static constexpr uint32_t kReadbackBufferSizeIncrement = 16 * 1024 * 1024;
|
||||
ID3D12Resource* readback_buffer_ = nullptr;
|
||||
dma::DMACJobHandle readback_available_ = 0;
|
||||
uint32_t readback_buffer_size_ = 0;
|
||||
|
||||
std::atomic<bool> pix_capture_requested_ = false;
|
||||
|
||||
@@ -407,15 +407,14 @@ bool D3D12SharedMemory::AllocateSparseHostGpuMemoryRange(
|
||||
|
||||
bool D3D12SharedMemory::UploadRanges(
|
||||
const std::pair<uint32_t, uint32_t>* upload_page_ranges,
|
||||
unsigned num_upload_page_ranges) {
|
||||
uint32_t num_upload_page_ranges) {
|
||||
if (!num_upload_page_ranges) {
|
||||
return true;
|
||||
}
|
||||
CommitUAVWritesAndTransitionBuffer(D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
command_processor_.SubmitBarriers();
|
||||
auto& command_list = command_processor_.GetDeferredCommandList();
|
||||
// for (auto upload_range : upload_page_ranges) {
|
||||
for (unsigned int i = 0; i < num_upload_page_ranges; ++i) {
|
||||
for (uint32_t i = 0; i < num_upload_page_ranges; ++i) {
|
||||
auto& upload_range = upload_page_ranges[i];
|
||||
uint32_t upload_range_start = upload_range.first;
|
||||
uint32_t upload_range_length = upload_range.second;
|
||||
@@ -437,7 +436,7 @@ bool D3D12SharedMemory::UploadRanges(
|
||||
uint32_t(upload_buffer_size), false, false);
|
||||
|
||||
if (upload_buffer_size < (1ULL << 32) && upload_buffer_size > 8192) {
|
||||
dma::vastcpy(
|
||||
memory::vastcpy(
|
||||
upload_buffer_mapping,
|
||||
memory().TranslatePhysical(upload_range_start << page_size_log2()),
|
||||
static_cast<uint32_t>(upload_buffer_size));
|
||||
|
||||
@@ -91,8 +91,8 @@ class D3D12SharedMemory : public SharedMemory {
|
||||
bool AllocateSparseHostGpuMemoryRange(uint32_t offset_allocations,
|
||||
uint32_t length_allocations) override;
|
||||
|
||||
bool UploadRanges(const std::pair<uint32_t, uint32_t>*
|
||||
upload_page_ranges, unsigned num_ranges) override;
|
||||
bool UploadRanges(const std::pair<uint32_t, uint32_t>* upload_page_ranges,
|
||||
uint32_t num_ranges) override;
|
||||
|
||||
private:
|
||||
D3D12CommandProcessor& command_processor_;
|
||||
|
||||
@@ -491,7 +491,7 @@ void D3D12TextureCache::RequestTextures(uint32_t used_texture_mask) {
|
||||
uint32_t textures_remaining = used_texture_mask;
|
||||
uint32_t index;
|
||||
while (xe::bit_scan_forward(textures_remaining, &index)) {
|
||||
textures_remaining &= ~(uint32_t(1) << index);
|
||||
textures_remaining = xe::clear_lowest_bit(textures_remaining);
|
||||
const TextureBinding* binding = GetValidTextureBinding(index);
|
||||
if (!binding) {
|
||||
continue;
|
||||
|
||||
@@ -9,21 +9,14 @@
|
||||
|
||||
#include "xenia/gpu/draw_util.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
#include "xenia/gpu/registers.h"
|
||||
#include "xenia/gpu/texture_cache.h"
|
||||
#include "xenia/gpu/texture_info.h"
|
||||
#include "xenia/gpu/texture_util.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/graphics_util.h"
|
||||
|
||||
// Very prominent in 545407F2.
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/gpu/register_file.h"
|
||||
#include "xenia/gpu/registers.h"
|
||||
#include "xenia/gpu/shader.h"
|
||||
#include "xenia/gpu/trace_writer.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
|
||||
|
||||
void ExecuteIndirectBuffer(uint32_t ptr, uint32_t count) XE_RESTRICT;
|
||||
|
||||
virtual uint32_t ExecutePrimaryBuffer(uint32_t start_index, uint32_t end_index) XE_RESTRICT;
|
||||
virtual bool ExecutePacket();
|
||||
XE_NOINLINE
|
||||
|
||||
public:
|
||||
void ExecutePacket(uint32_t ptr, uint32_t count);
|
||||
|
||||
protected:
|
||||
XE_NOINLINE
|
||||
bool ExecutePacketType0( uint32_t packet) XE_RESTRICT;
|
||||
XE_NOINLINE
|
||||
bool ExecutePacketType1( uint32_t packet) XE_RESTRICT;
|
||||
@@ -103,4 +108,7 @@ uint32_t GetCurrentRingReadCount();
|
||||
|
||||
XE_NOINLINE
|
||||
XE_COLD
|
||||
bool ExecutePacketType3_CountOverflow(uint32_t count);
|
||||
bool ExecutePacketType3_CountOverflow(uint32_t count);
|
||||
XE_NOINLINE
|
||||
XE_COLD
|
||||
bool ExecutePacketType0_CountOverflow(uint32_t count);
|
||||
@@ -75,33 +75,45 @@ bool COMMAND_PROCESSOR::ExecutePacket() {
|
||||
}
|
||||
}
|
||||
XE_NOINLINE
|
||||
XE_COLD
|
||||
bool COMMAND_PROCESSOR::ExecutePacketType0_CountOverflow(uint32_t count) {
|
||||
XELOGE("ExecutePacketType0 overflow (read count {:08X}, packet count {:08X})",
|
||||
COMMAND_PROCESSOR::GetCurrentRingReadCount(),
|
||||
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
|
||||
*/
|
||||
XE_NOINLINE
|
||||
bool COMMAND_PROCESSOR::ExecutePacketType0(uint32_t packet) XE_RESTRICT {
|
||||
// Type-0 packet.
|
||||
// Write count registers in sequence to the registers starting at
|
||||
// (base_index << 2).
|
||||
|
||||
uint32_t count = ((packet >> 16) & 0x3FFF) + 1;
|
||||
if (COMMAND_PROCESSOR::GetCurrentRingReadCount() < count * sizeof(uint32_t)) {
|
||||
XELOGE(
|
||||
"ExecutePacketType0 overflow (read count {:08X}, packet count {:08X})",
|
||||
COMMAND_PROCESSOR::GetCurrentRingReadCount(), count * sizeof(uint32_t));
|
||||
return false;
|
||||
}
|
||||
|
||||
trace_writer_.WritePacketStart(uint32_t(reader_.read_ptr() - 4), 1 + count);
|
||||
|
||||
uint32_t base_index = (packet & 0x7FFF);
|
||||
uint32_t write_one_reg = (packet >> 15) & 0x1;
|
||||
if (COMMAND_PROCESSOR::GetCurrentRingReadCount() >=
|
||||
count * sizeof(uint32_t)) {
|
||||
trace_writer_.WritePacketStart(uint32_t(reader_.read_ptr() - 4), 1 + count);
|
||||
|
||||
if (!write_one_reg) {
|
||||
COMMAND_PROCESSOR::WriteRegisterRangeFromRing(&reader_, base_index, count);
|
||||
uint32_t base_index = (packet & 0x7FFF);
|
||||
uint32_t write_one_reg = (packet >> 15) & 0x1;
|
||||
|
||||
if (!write_one_reg) {
|
||||
COMMAND_PROCESSOR::WriteRegisterRangeFromRing(&reader_, base_index,
|
||||
count);
|
||||
|
||||
} else {
|
||||
COMMAND_PROCESSOR::WriteOneRegisterFromRing(base_index, count);
|
||||
}
|
||||
|
||||
trace_writer_.WritePacketEnd();
|
||||
return true;
|
||||
} else {
|
||||
COMMAND_PROCESSOR::WriteOneRegisterFromRing(base_index, count);
|
||||
return COMMAND_PROCESSOR::ExecutePacketType0_CountOverflow(count);
|
||||
}
|
||||
|
||||
trace_writer_.WritePacketEnd();
|
||||
return true;
|
||||
}
|
||||
XE_NOINLINE
|
||||
bool COMMAND_PROCESSOR::ExecutePacketType1(uint32_t packet) XE_RESTRICT {
|
||||
@@ -430,6 +442,11 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_INDIRECT_BUFFER(
|
||||
|
||||
XE_NOINLINE
|
||||
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.
|
||||
@@ -1058,6 +1075,10 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_IM_LOAD_IMMEDIATE(
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
todo: shouldn't this do something?
|
||||
*/
|
||||
|
||||
bool COMMAND_PROCESSOR::ExecutePacketType3_INVALIDATE_STATE(
|
||||
uint32_t packet, uint32_t count) XE_RESTRICT {
|
||||
// selective invalidation of state pointers
|
||||
@@ -1099,3 +1120,76 @@ bool COMMAND_PROCESSOR::ExecutePacketType3_VIZ_QUERY(
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t COMMAND_PROCESSOR::ExecutePrimaryBuffer(uint32_t read_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
|
||||
// all commands.
|
||||
if (!trace_writer_.is_open() && trace_state_ == TraceState::kStreaming) {
|
||||
uint32_t title_id = kernel_state_->GetExecutableModule()
|
||||
? kernel_state_->GetExecutableModule()->title_id()
|
||||
: 0;
|
||||
auto file_name = fmt::format("{:08X}_stream.xtr", title_id);
|
||||
auto path = trace_stream_path_ / file_name;
|
||||
trace_writer_.Open(path, title_id);
|
||||
InitializeTrace();
|
||||
}
|
||||
#endif
|
||||
// Adjust pointer base.
|
||||
uint32_t start_ptr = primary_buffer_ptr_ + read_index * sizeof(uint32_t);
|
||||
start_ptr = (primary_buffer_ptr_ & ~0x1FFFFFFF) | (start_ptr & 0x1FFFFFFF);
|
||||
uint32_t end_ptr = primary_buffer_ptr_ + write_index * sizeof(uint32_t);
|
||||
end_ptr = (primary_buffer_ptr_ & ~0x1FFFFFFF) | (end_ptr & 0x1FFFFFFF);
|
||||
|
||||
trace_writer_.WritePrimaryBufferStart(start_ptr, write_index - read_index);
|
||||
|
||||
// Execute commands!
|
||||
|
||||
RingBuffer old_reader = reader_;
|
||||
new (&reader_) RingBuffer(memory_->TranslatePhysical(primary_buffer_ptr_),
|
||||
primary_buffer_size_);
|
||||
|
||||
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 (!COMMAND_PROCESSOR::ExecutePacket()) {
|
||||
// This probably should be fatal - but we're going to continue anyways.
|
||||
XELOGE("**** PRIMARY RINGBUFFER: Failed to execute packet.");
|
||||
assert_always();
|
||||
break;
|
||||
}
|
||||
} while (reader_.read_count());
|
||||
|
||||
COMMAND_PROCESSOR::OnPrimaryBufferEnd();
|
||||
|
||||
trace_writer_.WritePrimaryBufferEnd();
|
||||
|
||||
reader_ = old_reader;
|
||||
return write_index;
|
||||
}
|
||||
|
||||
void COMMAND_PROCESSOR::ExecutePacket(uint32_t ptr, uint32_t count) {
|
||||
// Execute commands!
|
||||
RingBuffer old_reader = reader_;
|
||||
|
||||
new (&reader_)
|
||||
RingBuffer{memory_->TranslatePhysical(ptr), count * sizeof(uint32_t)};
|
||||
|
||||
reader_.set_write_offset(count * sizeof(uint32_t));
|
||||
|
||||
do {
|
||||
if (!COMMAND_PROCESSOR::ExecutePacket()) {
|
||||
XELOGE("**** ExecutePacket: Failed to execute packet.");
|
||||
assert_always();
|
||||
break;
|
||||
}
|
||||
} while (reader_.read_count());
|
||||
reader_ = old_reader;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#define XENIA_GPU_SAMPLER_INFO_H_
|
||||
|
||||
#include "xenia/gpu/shader.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "xenia/base/string_buffer.h"
|
||||
#include "xenia/gpu/registers.h"
|
||||
#include "xenia/gpu/ucode.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
@@ -13,13 +13,6 @@
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/byte_order.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/gpu/registers.h"
|
||||
#include "xenia/gpu/trace_writer.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
|
||||
@@ -14,12 +14,9 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/gpu/register_file.h"
|
||||
#include "xenia/gpu/shader.h"
|
||||
#include "xenia/gpu/trace_writer.h"
|
||||
#include "xenia/gpu/ucode.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/memory.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
@@ -9,15 +9,9 @@
|
||||
|
||||
#include "xenia/gpu/shader_translator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
@@ -11,16 +11,7 @@
|
||||
#define XENIA_GPU_SHADER_TRANSLATOR_H_
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/string_buffer.h"
|
||||
#include "xenia/gpu/registers.h"
|
||||
#include "xenia/gpu/shader.h"
|
||||
#include "xenia/gpu/ucode.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
@@ -10,15 +10,11 @@
|
||||
#include "xenia/gpu/shared_memory.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/bit_range.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/memory.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
@@ -10,12 +10,6 @@
|
||||
#ifndef XENIA_GPU_SHARED_MEMORY_H_
|
||||
#define XENIA_GPU_SHARED_MEMORY_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/mutex.h"
|
||||
#include "xenia/memory.h"
|
||||
|
||||
namespace xe {
|
||||
@@ -141,7 +135,7 @@ class SharedMemory {
|
||||
// overall bounds of pages to be uploaded.
|
||||
virtual bool UploadRanges(
|
||||
const std::pair<uint32_t, uint32_t>* upload_page_ranges,
|
||||
unsigned num_upload_ranges) = 0;
|
||||
uint32_t num_upload_ranges) = 0;
|
||||
|
||||
const std::vector<std::pair<uint32_t, uint32_t>>& trace_download_ranges() {
|
||||
return trace_download_ranges_;
|
||||
@@ -183,14 +177,10 @@ class SharedMemory {
|
||||
FixedVMemVector<MAX_UPLOAD_RANGES * sizeof(std::pair<uint32_t, uint32_t>)>
|
||||
upload_ranges_;
|
||||
|
||||
// GPU-written memory downloading for traces. <Start address, length>.
|
||||
std::vector<std::pair<uint32_t, uint32_t>> trace_download_ranges_;
|
||||
uint32_t trace_download_page_count_ = 0;
|
||||
|
||||
// Mutex between the guest memory subsystem and the command processor, to be
|
||||
// locked when checking or updating validity of pages/ranges and when firing
|
||||
// watches.
|
||||
xe::global_critical_region global_critical_region_;
|
||||
static constexpr xe::global_critical_region global_critical_region_{};
|
||||
|
||||
// ***************************************************************************
|
||||
// Things below should be fully protected by global_critical_region.
|
||||
@@ -266,6 +256,11 @@ class SharedMemory {
|
||||
uint32_t watch_node_current_pool_allocated_ = 0;
|
||||
WatchRange* watch_range_first_free_ = nullptr;
|
||||
WatchNode* watch_node_first_free_ = nullptr;
|
||||
|
||||
// GPU-written memory downloading for traces. <Start address, length>.
|
||||
std::vector<std::pair<uint32_t, uint32_t>> trace_download_ranges_;
|
||||
uint32_t trace_download_page_count_ = 0;
|
||||
|
||||
// Triggers the watches (global and per-range), removing triggered range
|
||||
// watches.
|
||||
void FireWatches(uint32_t page_first, uint32_t page_last,
|
||||
|
||||
@@ -9,21 +9,11 @@
|
||||
|
||||
#include "xenia/gpu/texture_cache.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/clock.h"
|
||||
#include "xenia/base/cvar.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
#include "xenia/gpu/register_file.h"
|
||||
#include "xenia/gpu/texture_info.h"
|
||||
#include "xenia/gpu/texture_util.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
DEFINE_int32(
|
||||
draw_resolution_scale_x, 1,
|
||||
@@ -332,9 +322,13 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
|
||||
uint32_t bindings_changed = 0;
|
||||
uint32_t textures_remaining = used_texture_mask & ~texture_bindings_in_sync_;
|
||||
uint32_t index = 0;
|
||||
|
||||
Texture* textures_to_load[64]; // max bits = 32, can be unsigned + signed
|
||||
// means max array size = 64
|
||||
uint32_t num_textures_to_load = 0;
|
||||
while (xe::bit_scan_forward(textures_remaining, &index)) {
|
||||
uint32_t index_bit = UINT32_C(1) << index;
|
||||
textures_remaining &= ~index_bit;
|
||||
textures_remaining = xe::clear_lowest_bit(textures_remaining);
|
||||
TextureBinding& binding = texture_bindings_[index];
|
||||
const auto& fetch = regs.Get<xenos::xe_gpu_texture_fetch_t>(
|
||||
XE_GPU_REG_SHADER_CONSTANT_FETCH_00_0 + index * 6);
|
||||
@@ -406,12 +400,15 @@ void TextureCache::RequestTextures(uint32_t used_texture_mask) {
|
||||
binding.texture_signed = nullptr;
|
||||
}
|
||||
if (load_unsigned_data && binding.texture != nullptr) {
|
||||
LoadTextureData(*binding.texture);
|
||||
textures_to_load[num_textures_to_load++] = binding.texture;
|
||||
}
|
||||
if (load_signed_data && binding.texture_signed != nullptr) {
|
||||
LoadTextureData(*binding.texture_signed);
|
||||
textures_to_load[num_textures_to_load++] = binding.texture_signed;
|
||||
}
|
||||
}
|
||||
|
||||
LoadTexturesData(textures_to_load, num_textures_to_load);
|
||||
|
||||
if (bindings_changed) {
|
||||
UpdateTextureBindingsImpl(bindings_changed);
|
||||
}
|
||||
@@ -643,7 +640,134 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) {
|
||||
texture->LogAction("Created");
|
||||
return texture;
|
||||
}
|
||||
void TextureCache::LoadTexturesData(Texture** textures, uint32_t n_textures) {
|
||||
assert_true(n_textures <= 64);
|
||||
if (n_textures < 2) {
|
||||
if (!n_textures) {
|
||||
return;
|
||||
} else {
|
||||
LoadTextureData(*textures[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t index_base_outdated = 0;
|
||||
uint64_t index_mips_outdated = 0;
|
||||
uint32_t nkept = 0;
|
||||
{
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
for (uint32_t i = 0; i < n_textures; ++i) {
|
||||
Texture* current = textures[i];
|
||||
|
||||
auto base_outdated = current->base_outdated(global_lock);
|
||||
auto mips_outdated = current->mips_outdated(global_lock);
|
||||
|
||||
index_base_outdated |= static_cast<uint64_t>(base_outdated) << i;
|
||||
index_mips_outdated |= static_cast<uint64_t>(mips_outdated) << i;
|
||||
if (!base_outdated && !mips_outdated) {
|
||||
textures[i] = nullptr;
|
||||
|
||||
} else {
|
||||
nkept++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nkept == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < n_textures; ++i) {
|
||||
Texture* p_texture = textures[i];
|
||||
if (!p_texture) {
|
||||
continue;
|
||||
}
|
||||
textures[i] = nullptr;
|
||||
Texture& texture = *p_texture;
|
||||
|
||||
TextureKey texture_key = texture.key();
|
||||
// Implementation may load multiple blocks at once via accesses of up to 128
|
||||
// bits (R32G32B32A32_UINT), so aligning the size to this value to make sure
|
||||
// if the texture is small (especially if it's linear), the last blocks
|
||||
// won't be cut off (hosts may return 0, 0, 0, 0 for the whole
|
||||
// R32G32B32A32_UINT access for the non-16-aligned tail even if 1...15 bytes
|
||||
// are actually provided for it).
|
||||
|
||||
// Request uploading of the texture data to the shared memory.
|
||||
// This is also necessary when resolution scaling is used - the texture
|
||||
// cache relies on shared memory for invalidation of both unscaled and
|
||||
// scaled textures. Plus a texture may be unscaled partially, when only a
|
||||
// portion of its pages is invalidated, in this case we'll need the texture
|
||||
// from the shared memory to load the unscaled parts.
|
||||
// TODO(Triang3l): Load unscaled parts.
|
||||
bool base_resolved = texture.GetBaseResolved();
|
||||
if (index_base_outdated & (1ULL << i)) {
|
||||
if (!shared_memory().RequestRange(
|
||||
texture_key.base_page << 12,
|
||||
xe::align(texture.GetGuestBaseSize(), UINT32_C(16)),
|
||||
texture_key.scaled_resolve ? nullptr : &base_resolved)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
bool mips_resolved = texture.GetMipsResolved();
|
||||
if (index_mips_outdated & (1ULL << i)) {
|
||||
if (!shared_memory().RequestRange(
|
||||
texture_key.mip_page << 12,
|
||||
xe::align(texture.GetGuestMipsSize(), UINT32_C(16)),
|
||||
texture_key.scaled_resolve ? nullptr : &mips_resolved)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (texture_key.scaled_resolve) {
|
||||
// Make sure all the scaled resolve memory is resident and accessible from
|
||||
// the shader, including any possible padding that hasn't yet been touched
|
||||
// by an actual resolve, but is still included in the texture size, so the
|
||||
// GPU won't be trying to access unmapped memory.
|
||||
if (!EnsureScaledResolveMemoryCommitted(texture_key.base_page << 12,
|
||||
texture.GetGuestBaseSize(), 4)) {
|
||||
continue;
|
||||
}
|
||||
if (!EnsureScaledResolveMemoryCommitted(texture_key.mip_page << 12,
|
||||
texture.GetGuestMipsSize(), 4)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Actually load the texture data.
|
||||
if (!LoadTextureDataFromResidentMemoryImpl(
|
||||
texture, (index_base_outdated & (1ULL << i)) != 0,
|
||||
(index_mips_outdated & (1ULL << i)) != 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update the source of the texture (resolve vs. CPU or memexport) for
|
||||
// purposes of handling piecewise gamma emulation via sRGB and for
|
||||
// resolution scale in sampling offsets.
|
||||
if (!texture_key.scaled_resolve) {
|
||||
texture.SetBaseResolved(base_resolved);
|
||||
texture.SetMipsResolved(mips_resolved);
|
||||
}
|
||||
// reque for makeuptodatandwatch
|
||||
textures[i] = &texture;
|
||||
}
|
||||
{
|
||||
auto crit = global_critical_region_.Acquire();
|
||||
|
||||
for (uint32_t i = 0; i < n_textures; ++i) {
|
||||
auto texture = textures[i];
|
||||
if (!texture) {
|
||||
continue;
|
||||
}
|
||||
// Mark the ranges as uploaded and watch them. This is needed for scaled
|
||||
// resolves as well to detect when the CPU wants to reuse the memory for a
|
||||
// regular texture or a vertex buffer, and thus the scaled resolve version
|
||||
// is not up to date anymore.
|
||||
texture->MakeUpToDateAndWatch(crit);
|
||||
|
||||
texture->LogAction("Loaded");
|
||||
}
|
||||
}
|
||||
}
|
||||
bool TextureCache::LoadTextureData(Texture& texture) {
|
||||
// Check what needs to be uploaded.
|
||||
bool base_outdated, mips_outdated;
|
||||
|
||||
@@ -559,6 +559,7 @@ class TextureCache {
|
||||
return load_shader_info_[load_shader_index];
|
||||
}
|
||||
bool LoadTextureData(Texture& texture);
|
||||
void LoadTexturesData(Texture** textures, uint32_t n_textures);
|
||||
// Writes the texture data (for base, mips or both - but not neither) from the
|
||||
// shared memory or the scaled resolve memory. The shared memory management is
|
||||
// done outside this function, the implementation just needs to load the data
|
||||
|
||||
@@ -8,14 +8,7 @@
|
||||
*/
|
||||
|
||||
#include "xenia/gpu/texture_info.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/base/xxhash.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
@@ -8,13 +8,6 @@
|
||||
*/
|
||||
|
||||
#include "xenia/gpu/texture_util.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/math.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace texture_util {
|
||||
|
||||
@@ -10,11 +10,6 @@
|
||||
#ifndef XENIA_GPU_UCODE_H_
|
||||
#define XENIA_GPU_UCODE_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/platform.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
// The XNA Game Studio 3.1 contains Graphics.ShaderCompiler.AssembleFromSource,
|
||||
|
||||
@@ -46,6 +46,9 @@ namespace gpu {
|
||||
namespace vulkan {
|
||||
|
||||
class VulkanCommandProcessor final : public CommandProcessor {
|
||||
protected:
|
||||
#include "../pm4_command_processor_declare.h"
|
||||
|
||||
public:
|
||||
// Single-descriptor layouts for use within a single frame.
|
||||
enum class SingleTransientDescriptorLayout {
|
||||
@@ -53,7 +56,6 @@ class VulkanCommandProcessor final : public CommandProcessor {
|
||||
kStorageBufferCompute,
|
||||
kCount,
|
||||
};
|
||||
#include "../pm4_command_processor_declare.h"
|
||||
|
||||
class ScratchBufferAcquisition {
|
||||
public:
|
||||
|
||||
@@ -377,7 +377,7 @@ bool VulkanSharedMemory::AllocateSparseHostGpuMemoryRange(
|
||||
|
||||
bool VulkanSharedMemory::UploadRanges(
|
||||
const std::pair<uint32_t, uint32_t>* upload_page_ranges,
|
||||
unsigned num_upload_ranges) {
|
||||
uint32_t num_upload_ranges) {
|
||||
if (!num_upload_ranges) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ class VulkanSharedMemory : public SharedMemory {
|
||||
bool AllocateSparseHostGpuMemoryRange(uint32_t offset_allocations,
|
||||
uint32_t length_allocations) override;
|
||||
|
||||
bool UploadRanges(const std::pair<uint32_t, uint32_t>*
|
||||
upload_page_ranges, unsigned num_ranges) override;
|
||||
bool UploadRanges(const std::pair<uint32_t, uint32_t>* upload_page_ranges,
|
||||
uint32_t num_ranges) override;
|
||||
|
||||
private:
|
||||
void GetUsageMasks(Usage usage, VkPipelineStageFlags& stage_mask,
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "xenia/base/math.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace xenos {
|
||||
|
||||
@@ -10,13 +10,10 @@
|
||||
#ifndef XENIA_GPU_XENOS_H_
|
||||
#define XENIA_GPU_XENOS_H_
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/byte_order.h"
|
||||
#include "xenia/base/math.h"
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/base/platform.h"
|
||||
#include "xenia/base/math.h"
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
Reference in New Issue
Block a user