[D3D12] Trace guest memory operations

This commit is contained in:
Triang3l
2019-10-23 23:33:50 +03:00
parent e07b0ed2ad
commit 4623b41023
17 changed files with 269 additions and 33 deletions

View File

@@ -672,7 +672,8 @@ bool D3D12CommandProcessor::SetupContext() {
sampler_heap_pool_ = std::make_unique<ui::d3d12::DescriptorHeapPool>(
context, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 2048);
shared_memory_ = std::make_unique<SharedMemory>(this, memory_);
shared_memory_ =
std::make_unique<SharedMemory>(this, memory_, &trace_writer_);
if (!shared_memory_->Initialize()) {
XELOGE("Failed to initialize shared memory");
return false;
@@ -700,8 +701,8 @@ bool D3D12CommandProcessor::SetupContext() {
return false;
}
primitive_converter_ =
std::make_unique<PrimitiveConverter>(this, register_file_, memory_);
primitive_converter_ = std::make_unique<PrimitiveConverter>(
this, register_file_, memory_, &trace_writer_);
if (!primitive_converter_->Initialize()) {
XELOGE("Failed to initialize the geometric primitive converter");
return false;
@@ -1655,6 +1656,19 @@ bool D3D12CommandProcessor::IssueDraw(PrimitiveType primitive_type,
return true;
}
void D3D12CommandProcessor::InitializeTrace() {
BeginFrame();
bool anySubmitted = false;
anySubmitted |= shared_memory_->InitializeTraceSubmitDownloads();
if (anySubmitted) {
EndFrame();
GetD3D12Context()->AwaitAllFramesCompletion();
shared_memory_->InitializeTraceCompleteDownloads();
}
}
void D3D12CommandProcessor::FinalizeTrace() {}
bool D3D12CommandProcessor::IssueCopy() {
#if FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_f("gpu");

View File

@@ -158,6 +158,9 @@ class D3D12CommandProcessor : public CommandProcessor {
IndexBufferInfo* index_buffer_info) override;
bool IssueCopy() override;
void InitializeTrace() override;
void FinalizeTrace() override;
private:
enum RootParameter : UINT {
// These are always present.

View File

@@ -41,10 +41,12 @@ constexpr uint32_t PrimitiveConverter::kStaticIBTotalCount;
PrimitiveConverter::PrimitiveConverter(D3D12CommandProcessor* command_processor,
RegisterFile* register_file,
Memory* memory)
Memory* memory,
TraceWriter* trace_writer)
: command_processor_(command_processor),
register_file_(register_file),
memory_(memory) {
memory_(memory),
trace_writer_(trace_writer) {
system_page_size_ = uint32_t(memory::page_size());
}
@@ -248,6 +250,7 @@ PrimitiveConverter::ConversionResult PrimitiveConverter::ConvertPrimitives(
address &= index_32bit ? 0x1FFFFFFC : 0x1FFFFFFE;
uint32_t index_size = index_32bit ? sizeof(uint32_t) : sizeof(uint16_t);
uint32_t index_buffer_size = index_size * index_count;
uint32_t address_last = address + index_size * (index_count - 1);
// Create the cache entry, currently only for the key.
@@ -305,6 +308,7 @@ PrimitiveConverter::ConversionResult PrimitiveConverter::ConvertPrimitives(
if (source_type == PrimitiveType::kTriangleFan) {
// Triangle fans are not supported by Direct3D 12 at all.
conversion_needed = true;
trace_writer_->WriteMemoryRead(address, index_buffer_size);
if (reset) {
uint32_t current_fan_index_count = 0;
for (uint32_t i = 0; i < index_count; ++i) {
@@ -327,6 +331,7 @@ PrimitiveConverter::ConversionResult PrimitiveConverter::ConvertPrimitives(
// Check if the restart index is used at all in this buffer because reading
// vertices from a default heap is faster than from an upload heap.
conversion_needed = false;
trace_writer_->WriteMemoryRead(address, index_buffer_size);
#if XE_ARCH_AMD64
// Will use SIMD to copy 16-byte blocks using _mm_or_si128.
simd = true;
@@ -412,6 +417,7 @@ PrimitiveConverter::ConversionResult PrimitiveConverter::ConvertPrimitives(
#endif // XE_ARCH_AMD64
} else if (source_type == PrimitiveType::kLineLoop) {
conversion_needed = true;
trace_writer_->WriteMemoryRead(address, index_buffer_size);
if (reset) {
reset_actually_used = false;
uint32_t current_strip_index_count = 0;
@@ -437,6 +443,7 @@ PrimitiveConverter::ConversionResult PrimitiveConverter::ConvertPrimitives(
}
} else if (source_type == PrimitiveType::kQuadList) {
conversion_needed = true;
trace_writer_->WriteMemoryRead(address, index_buffer_size);
converted_index_count = (index_count >> 2) * 6;
}
converted_indices.converted_index_count = converted_index_count;
@@ -739,6 +746,12 @@ D3D12_GPU_VIRTUAL_ADDRESS PrimitiveConverter::GetStaticIndexBuffer(
return D3D12_GPU_VIRTUAL_ADDRESS(0);
}
void PrimitiveConverter::InitializeTrace() {
// WriteMemoryRead must not be skipped.
converted_indices_cache_.clear();
memory_regions_used_ = 0;
}
} // namespace d3d12
} // namespace gpu
} // namespace xe

View File

@@ -15,6 +15,7 @@
#include <unordered_map>
#include "xenia/gpu/register_file.h"
#include "xenia/gpu/trace_writer.h"
#include "xenia/gpu/xenos.h"
#include "xenia/memory.h"
#include "xenia/ui/d3d12/d3d12_context.h"
@@ -37,7 +38,8 @@ class D3D12CommandProcessor;
class PrimitiveConverter {
public:
PrimitiveConverter(D3D12CommandProcessor* command_processor,
RegisterFile* register_file, Memory* memory);
RegisterFile* register_file, Memory* memory,
TraceWriter* trace_writer);
~PrimitiveConverter();
bool Initialize();
@@ -80,6 +82,8 @@ class PrimitiveConverter {
PrimitiveType source_type, uint32_t index_count,
uint32_t& index_count_out) const;
void InitializeTrace();
private:
// simd_offset is source address & 15 - if SIMD is used, the source and the
// target must have the same alignment within one register. 0 is optimal when
@@ -98,6 +102,7 @@ class PrimitiveConverter {
D3D12CommandProcessor* command_processor_;
RegisterFile* register_file_;
Memory* memory_;
TraceWriter* trace_writer_;
std::unique_ptr<ui::d3d12::UploadBufferPool> buffer_pool_ = nullptr;

View File

@@ -11,6 +11,7 @@
#include <algorithm>
#include <cstring>
#include <vector>
#include "xenia/base/assert.h"
#include "xenia/base/cvar.h"
@@ -42,8 +43,10 @@ constexpr uint32_t SharedMemory::kWatchRangePoolSize;
constexpr uint32_t SharedMemory::kWatchNodePoolSize;
SharedMemory::SharedMemory(D3D12CommandProcessor* command_processor,
Memory* memory)
: command_processor_(command_processor), memory_(memory) {
Memory* memory, TraceWriter* trace_writer)
: command_processor_(command_processor),
memory_(memory),
trace_writer_(trace_writer) {
page_size_log2_ = xe::log2_ceil(uint32_t(xe::memory::page_size()));
page_count_ = kBufferSize >> page_size_log2_;
uint32_t page_bitmap_length = page_count_ >> 6;
@@ -133,10 +136,14 @@ bool SharedMemory::Initialize() {
physical_write_watch_handle_ =
memory_->RegisterPhysicalWriteWatch(MemoryWriteCallbackThunk, this);
ResetTraceGPUWrittenBuffer();
return true;
}
void SharedMemory::Shutdown() {
ResetTraceGPUWrittenBuffer();
// TODO(Triang3l): Do something in case any watches are still registered.
if (physical_write_watch_handle_ != nullptr) {
@@ -365,6 +372,8 @@ bool SharedMemory::RequestRange(uint32_t start, uint32_t length) {
for (auto upload_range : upload_ranges_) {
uint32_t upload_range_start = upload_range.first;
uint32_t upload_range_length = upload_range.second;
trace_writer_->WriteMemoryRead(upload_range_start << page_size_log2_,
upload_range_length << page_size_log2_);
while (upload_range_length != 0) {
ID3D12Resource* upload_buffer;
uint32_t upload_buffer_offset, upload_buffer_size;
@@ -376,7 +385,6 @@ bool SharedMemory::RequestRange(uint32_t start, uint32_t length) {
return false;
}
uint32_t upload_buffer_pages = upload_buffer_size >> page_size_log2_;
// No mutex holding here!
MakeRangeValid(upload_range_start, upload_buffer_pages, false);
std::memcpy(
upload_buffer_mapping,
@@ -441,7 +449,6 @@ void SharedMemory::RangeWrittenByGPU(uint32_t start, uint32_t length) {
// Mark the range as valid (so pages are not reuploaded until modified by the
// CPU) and watch it so the CPU can reuse it and this will be caught.
// No mutex holding here!
MakeRangeValid(page_first, page_last - page_first + 1, true);
}
@@ -654,6 +661,157 @@ void SharedMemory::WriteRawUAVDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE handle) {
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
}
bool SharedMemory::InitializeTraceSubmitDownloads() {
// Invalidate the entire memory CPU->GPU memory copy so all the history
// doesn't have to be written into every frame trace, and collect the list of
// ranges with data modified on the GPU.
ResetTraceGPUWrittenBuffer();
uint32_t gpu_written_page_count = 0;
{
auto global_lock = global_critical_region_.Acquire();
uint32_t fire_watches_range_start = UINT32_MAX;
uint32_t gpu_written_range_start = UINT32_MAX;
for (uint32_t i = 0; i * 2 < valid_and_gpu_written_pages_.size(); ++i) {
uint64_t previously_valid_block = valid_and_gpu_written_pages_[i * 2];
uint64_t gpu_written_block = valid_and_gpu_written_pages_[i * 2 + 1];
valid_and_gpu_written_pages_[i * 2] = gpu_written_block;
// Fire watches on the invalidated pages.
uint64_t fire_watches_block = previously_valid_block & ~gpu_written_block;
uint64_t fire_watches_break_block = ~fire_watches_block;
while (true) {
uint32_t fire_watches_block_page;
if (!xe::bit_scan_forward(fire_watches_range_start == UINT32_MAX
? fire_watches_block
: fire_watches_break_block,
&fire_watches_block_page)) {
break;
}
uint32_t fire_watches_page = (i << 6) + fire_watches_block_page;
if (fire_watches_range_start == UINT32_MAX) {
fire_watches_range_start = fire_watches_page;
} else {
FireWatches(fire_watches_range_start, fire_watches_page - 1, false);
fire_watches_range_start = UINT32_MAX;
}
uint64_t fire_watches_block_mask =
~((1ull << fire_watches_block_page) - 1);
fire_watches_block &= fire_watches_block_mask;
fire_watches_break_block &= fire_watches_block_mask;
}
// Add to the GPU-written ranges.
uint64_t gpu_written_break_block = ~gpu_written_block;
while (true) {
uint32_t gpu_written_block_page;
if (!xe::bit_scan_forward(gpu_written_range_start == UINT32_MAX
? gpu_written_block
: gpu_written_break_block,
&gpu_written_block_page)) {
break;
}
uint32_t gpu_written_page = (i << 6) + gpu_written_block_page;
if (gpu_written_range_start == UINT32_MAX) {
gpu_written_range_start = gpu_written_page;
} else {
uint32_t gpu_written_range_length =
gpu_written_page - gpu_written_range_start;
trace_gpu_written_ranges_.push_back(
std::make_pair(gpu_written_range_start << page_size_log2_,
gpu_written_range_length << page_size_log2_));
gpu_written_page_count += gpu_written_range_length;
gpu_written_range_start = UINT32_MAX;
}
uint64_t gpu_written_block_mask =
~((1ull << gpu_written_block_page) - 1);
gpu_written_block &= gpu_written_block_mask;
gpu_written_break_block &= gpu_written_block_mask;
}
}
if (fire_watches_range_start != UINT32_MAX) {
FireWatches(fire_watches_range_start, page_count_ - 1, false);
}
if (gpu_written_range_start != UINT32_MAX) {
uint32_t gpu_written_range_length = page_count_ - gpu_written_range_start;
trace_gpu_written_ranges_.push_back(
std::make_pair(gpu_written_range_start << page_size_log2_,
gpu_written_range_length << page_size_log2_));
gpu_written_page_count += gpu_written_range_length;
}
}
// Request downloading of GPU-written memory.
if (!gpu_written_page_count) {
return false;
}
D3D12_RESOURCE_DESC gpu_written_buffer_desc;
ui::d3d12::util::FillBufferResourceDesc(
gpu_written_buffer_desc, gpu_written_page_count << page_size_log2_,
D3D12_RESOURCE_FLAG_NONE);
auto device =
command_processor_->GetD3D12Context()->GetD3D12Provider()->GetDevice();
if (FAILED(device->CreateCommittedResource(
&ui::d3d12::util::kHeapPropertiesReadback, D3D12_HEAP_FLAG_NONE,
&gpu_written_buffer_desc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
IID_PPV_ARGS(&trace_gpu_written_buffer_)))) {
XELOGE(
"Failed to create a %u KB GPU-written memory download buffer for frame "
"tracing",
gpu_written_page_count << page_size_log2_ >> 10);
ResetTraceGPUWrittenBuffer();
return false;
}
auto command_list = command_processor_->GetDeferredCommandList();
UseAsCopySource();
command_processor_->SubmitBarriers();
uint32_t gpu_written_buffer_offset = 0;
for (auto& gpu_written_submit_range : trace_gpu_written_ranges_) {
// For cases like resolution scale, when the data may not be actually
// written, just marked as valid.
if (!MakeTilesResident(gpu_written_submit_range.first,
gpu_written_submit_range.second)) {
gpu_written_submit_range.second = 0;
continue;
}
command_list->D3DCopyBufferRegion(
trace_gpu_written_buffer_, gpu_written_buffer_offset, buffer_,
gpu_written_submit_range.first, gpu_written_submit_range.second);
gpu_written_buffer_offset += gpu_written_submit_range.second;
}
return true;
}
void SharedMemory::InitializeTraceCompleteDownloads() {
if (!trace_gpu_written_buffer_) {
return;
}
void* download_mapping;
if (SUCCEEDED(
trace_gpu_written_buffer_->Map(0, nullptr, &download_mapping))) {
uint32_t gpu_written_buffer_offset = 0;
for (auto gpu_written_submit_range : trace_gpu_written_ranges_) {
trace_writer_->WriteMemoryWrite(
gpu_written_submit_range.first, gpu_written_submit_range.second,
reinterpret_cast<const uint8_t*>(download_mapping) +
gpu_written_buffer_offset);
}
D3D12_RANGE download_write_range = {};
trace_gpu_written_buffer_->Unmap(0, &download_write_range);
} else {
XELOGE(
"Failed to map the GPU-written memory download buffer for frame "
"tracing");
}
ResetTraceGPUWrittenBuffer();
}
void SharedMemory::ResetTraceGPUWrittenBuffer() {
trace_gpu_written_ranges_.clear();
trace_gpu_written_ranges_.shrink_to_fit();
ui::d3d12::util::ReleaseAndNull(trace_gpu_written_buffer_);
}
} // namespace d3d12
} // namespace gpu
} // namespace xe

View File

@@ -12,9 +12,11 @@
#include <memory>
#include <mutex>
#include <utility>
#include <vector>
#include "xenia/base/mutex.h"
#include "xenia/gpu/trace_writer.h"
#include "xenia/memory.h"
#include "xenia/ui/d3d12/d3d12_api.h"
#include "xenia/ui/d3d12/pools.h"
@@ -30,7 +32,8 @@ class D3D12CommandProcessor;
// system page size granularity.
class SharedMemory {
public:
SharedMemory(D3D12CommandProcessor* command_processor, Memory* memory);
SharedMemory(D3D12CommandProcessor* command_processor, Memory* memory,
TraceWriter* trace_writer);
~SharedMemory();
bool Initialize();
@@ -124,6 +127,10 @@ class SharedMemory {
void WriteRawSRVDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE handle);
void WriteRawUAVDescriptor(D3D12_CPU_DESCRIPTOR_HANDLE handle);
// Returns true if any downloads were submitted to the command processor.
bool InitializeTraceSubmitDownloads();
void InitializeTraceCompleteDownloads();
private:
bool AreTiledResourcesUsed() const;
@@ -132,8 +139,8 @@ class SharedMemory {
bool written_by_gpu);
D3D12CommandProcessor* command_processor_;
Memory* memory_;
TraceWriter* trace_writer_;
// The 512 MB tiled buffer.
static constexpr uint32_t kBufferSizeLog2 = 29;
@@ -268,6 +275,13 @@ class SharedMemory {
std::unique_ptr<ui::d3d12::UploadBufferPool> upload_buffer_pool_ = nullptr;
void TransitionBuffer(D3D12_RESOURCE_STATES new_state);
// GPU-written memory downloading for traces.
// Start page, length in pages.
std::vector<std::pair<uint32_t, uint32_t>> trace_gpu_written_ranges_;
// Created temporarily, only for downloading.
ID3D12Resource* trace_gpu_written_buffer_ = nullptr;
void ResetTraceGPUWrittenBuffer();
};
} // namespace d3d12