/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2018 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #ifndef XENIA_GPU_D3D12_SHARED_MEMORY_H_ #define XENIA_GPU_D3D12_SHARED_MEMORY_H_ #include #include #include "xenia/memory.h" #include "xenia/ui/d3d12/d3d12_api.h" #include "xenia/ui/d3d12/d3d12_context.h" #include "xenia/ui/d3d12/pools.h" namespace xe { namespace gpu { namespace d3d12 { // Manages memory for unconverted textures, resolve targets, vertex and index // buffers that can be accessed from shaders with Xenon physical addresses, with // system page size granularity. class SharedMemory { public: SharedMemory(Memory* memory, ui::d3d12::D3D12Context* context); ~SharedMemory(); bool Initialize(); void Shutdown(); D3D12_GPU_VIRTUAL_ADDRESS GetGPUAddress() const { return buffer_gpu_address_; } void BeginFrame(); // Returns true if anything has been written to command_list been done. // The draw command list is needed for the transition. bool EndFrame(ID3D12GraphicsCommandList* command_list_setup, ID3D12GraphicsCommandList* command_list_draw); // Marks the range as used in this frame, queues it for upload if it was // modified. Ensures the backing memory for the address range is present in // the tiled buffer, allocating if needed. If couldn't allocate, false is // returned - it's unsafe to use this portion (on tiled resources tier 1 at // least). bool UseRange(uint32_t start, uint32_t length); // Makes the buffer usable for vertices, indices and texture untiling. void UseForReading(ID3D12GraphicsCommandList* command_list); // Makes the buffer usable for texture tiling after a resolve. void UseForWriting(ID3D12GraphicsCommandList* command_list); void CreateSRV(D3D12_CPU_DESCRIPTOR_HANDLE handle); void CreateUAV(D3D12_CPU_DESCRIPTOR_HANDLE handle); private: Memory* memory_; ui::d3d12::D3D12Context* context_; // The 512 MB tiled buffer. static constexpr uint32_t kBufferSizeLog2 = 29; static constexpr uint32_t kBufferSize = 1 << kBufferSizeLog2; static constexpr uint32_t kAddressMask = kBufferSize - 1; ID3D12Resource* buffer_ = nullptr; D3D12_GPU_VIRTUAL_ADDRESS buffer_gpu_address_ = 0; D3D12_RESOURCE_STATES buffer_state_ = D3D12_RESOURCE_STATE_COPY_DEST; // D3D resource tiles are 64 KB in size. static constexpr uint32_t kTileSizeLog2 = 16; static constexpr uint32_t kTileSize = 1 << kTileSizeLog2; // Heaps are 16 MB, so not too many of them are allocated. static constexpr uint32_t kHeapSizeLog2 = 24; static constexpr uint32_t kHeapSize = 1 << kHeapSizeLog2; // Resident portions of the tiled buffer. ID3D12Heap* heaps_[kBufferSize >> kHeapSizeLog2] = {}; // Whether creation of a heap has failed in the current frame. bool heap_creation_failed_ = false; // Log2 of system page size. uint32_t page_size_log2_; // Total physical page count. uint32_t page_count_; // Bit vector containing whether physical memory system pages are up to date. std::vector pages_in_sync_; // Mutex for the watched pages and the triggered watches. std::mutex watch_mutex_; // Whether each physical page is watched by the GPU (after uploading). // Once a watch is triggered, it's not watched anymore. std::vector watched_pages_; // Whether each page was modified while the current frame is being processed. // This is checked and cleared in the beginning of a GPU frame. // Because this is done with a locked CPU-GPU mutex, it's stored in 2 levels, // so unmodified pages can be skipped quickly, and clearing is also fast. // On L1, each bit corresponds to a single page, on L2, to 64 pages. // Checking if L2 is non-zero before accessing L1 is REQUIRED since L1 is not // cleared! std::vector watches_triggered_l1_; std::vector watches_triggered_l2_; // Memory access callback. static bool WatchCallbackThunk(void* context_ptr, uint32_t address); bool WatchCallback(uint32_t address); // Pages that need to be uploaded in this frame (that are used but modified). std::vector upload_pages_; uint32_t NextUploadRange(uint32_t search_start, uint32_t& length) const; std::unique_ptr upload_buffer_pool_ = nullptr; void TransitionBuffer(D3D12_RESOURCE_STATES new_state, ID3D12GraphicsCommandList* command_list); }; } // namespace d3d12 } // namespace gpu } // namespace xe #endif // XENIA_GPU_D3D12_SHARED_MEMORY_H_