Vulkan: Properly invalidate resolve textures
Trace Writer memory caching
This commit is contained in:
parent
070e7bf33d
commit
c62c4fe741
|
@ -47,6 +47,7 @@ bool TraceWriter::Open(const std::wstring& path, uint32_t title_id) {
|
||||||
header.title_id = title_id;
|
header.title_id = title_id;
|
||||||
fwrite(&header, sizeof(header), 1, file_);
|
fwrite(&header, sizeof(header), 1, file_);
|
||||||
|
|
||||||
|
cached_memory_reads_.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +59,8 @@ void TraceWriter::Flush() {
|
||||||
|
|
||||||
void TraceWriter::Close() {
|
void TraceWriter::Close() {
|
||||||
if (file_) {
|
if (file_) {
|
||||||
|
cached_memory_reads_.clear();
|
||||||
|
|
||||||
fflush(file_);
|
fflush(file_);
|
||||||
fclose(file_);
|
fclose(file_);
|
||||||
file_ = nullptr;
|
file_ = nullptr;
|
||||||
|
@ -132,6 +135,31 @@ void TraceWriter::WriteMemoryRead(uint32_t base_ptr, size_t length) {
|
||||||
WriteMemoryCommand(TraceCommandType::kMemoryRead, base_ptr, length);
|
WriteMemoryCommand(TraceCommandType::kMemoryRead, base_ptr, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TraceWriter::WriteMemoryReadCached(uint32_t base_ptr, size_t length) {
|
||||||
|
if (!file_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK: length is guaranteed to be within 32-bits (guest memory)
|
||||||
|
uint64_t key = uint64_t(base_ptr) << 32 | uint64_t(length);
|
||||||
|
if (cached_memory_reads_.find(key) == cached_memory_reads_.end()) {
|
||||||
|
WriteMemoryCommand(TraceCommandType::kMemoryRead, base_ptr, length);
|
||||||
|
cached_memory_reads_.insert(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TraceWriter::WriteMemoryReadCachedNop(uint32_t base_ptr, size_t length) {
|
||||||
|
if (!file_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK: length is guaranteed to be within 32-bits (guest memory)
|
||||||
|
uint64_t key = uint64_t(base_ptr) << 32 | uint64_t(length);
|
||||||
|
if (cached_memory_reads_.find(key) == cached_memory_reads_.end()) {
|
||||||
|
cached_memory_reads_.insert(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TraceWriter::WriteMemoryWrite(uint32_t base_ptr, size_t length) {
|
void TraceWriter::WriteMemoryWrite(uint32_t base_ptr, size_t length) {
|
||||||
if (!file_) {
|
if (!file_) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#ifndef XENIA_GPU_TRACE_WRITER_H_
|
#ifndef XENIA_GPU_TRACE_WRITER_H_
|
||||||
#define XENIA_GPU_TRACE_WRITER_H_
|
#define XENIA_GPU_TRACE_WRITER_H_
|
||||||
|
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "xenia/base/filesystem.h"
|
#include "xenia/base/filesystem.h"
|
||||||
|
@ -36,6 +37,8 @@ class TraceWriter {
|
||||||
void WritePacketStart(uint32_t base_ptr, uint32_t count);
|
void WritePacketStart(uint32_t base_ptr, uint32_t count);
|
||||||
void WritePacketEnd();
|
void WritePacketEnd();
|
||||||
void WriteMemoryRead(uint32_t base_ptr, size_t length);
|
void WriteMemoryRead(uint32_t base_ptr, size_t length);
|
||||||
|
void WriteMemoryReadCached(uint32_t base_ptr, size_t length);
|
||||||
|
void WriteMemoryReadCachedNop(uint32_t base_ptr, size_t length);
|
||||||
void WriteMemoryWrite(uint32_t base_ptr, size_t length);
|
void WriteMemoryWrite(uint32_t base_ptr, size_t length);
|
||||||
void WriteEvent(EventCommand::Type event_type);
|
void WriteEvent(EventCommand::Type event_type);
|
||||||
|
|
||||||
|
@ -43,6 +46,7 @@ class TraceWriter {
|
||||||
void WriteMemoryCommand(TraceCommandType type, uint32_t base_ptr,
|
void WriteMemoryCommand(TraceCommandType type, uint32_t base_ptr,
|
||||||
size_t length);
|
size_t length);
|
||||||
|
|
||||||
|
std::set<uint64_t> cached_memory_reads_;
|
||||||
uint8_t* membase_;
|
uint8_t* membase_;
|
||||||
FILE* file_;
|
FILE* file_;
|
||||||
|
|
||||||
|
|
|
@ -348,9 +348,8 @@ TextureCache::Texture* TextureCache::AllocateTexture(
|
||||||
// Check the device limits for the format before we create it.
|
// Check the device limits for the format before we create it.
|
||||||
VkFormatProperties props;
|
VkFormatProperties props;
|
||||||
vkGetPhysicalDeviceFormatProperties(*device_, format, &props);
|
vkGetPhysicalDeviceFormatProperties(*device_, format, &props);
|
||||||
uint32_t required_flags = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
|
uint32_t required_flags =
|
||||||
VK_FORMAT_FEATURE_BLIT_DST_BIT |
|
VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT;
|
||||||
VK_FORMAT_FEATURE_BLIT_SRC_BIT;
|
|
||||||
if ((props.optimalTilingFeatures & required_flags) != required_flags) {
|
if ((props.optimalTilingFeatures & required_flags) != required_flags) {
|
||||||
// Texture needs conversion on upload to a native format.
|
// Texture needs conversion on upload to a native format.
|
||||||
// assert_always();
|
// assert_always();
|
||||||
|
@ -484,10 +483,13 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info,
|
||||||
if (it->second->texture_info == texture_info) {
|
if (it->second->texture_info == texture_info) {
|
||||||
if (it->second->pending_invalidation) {
|
if (it->second->pending_invalidation) {
|
||||||
// This texture has been invalidated!
|
// This texture has been invalidated!
|
||||||
Scavenge();
|
RemoveInvalidatedTextures();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace_writer_->WriteMemoryReadCached(texture_info.guest_address,
|
||||||
|
texture_info.input_length);
|
||||||
|
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,6 +503,12 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info,
|
||||||
texture->texture_info.size_2d.logical_width &&
|
texture->texture_info.size_2d.logical_width &&
|
||||||
texture_info.size_2d.logical_height ==
|
texture_info.size_2d.logical_height ==
|
||||||
texture->texture_info.size_2d.logical_height) {
|
texture->texture_info.size_2d.logical_height) {
|
||||||
|
if (texture->pending_invalidation) {
|
||||||
|
// Texture invalidated! Remove.
|
||||||
|
RemoveInvalidatedTextures();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Exact match.
|
// Exact match.
|
||||||
// TODO: Lazy match (at an offset)
|
// TODO: Lazy match (at an offset)
|
||||||
// Upgrade this texture to a full texture.
|
// Upgrade this texture to a full texture.
|
||||||
|
@ -511,6 +519,10 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info,
|
||||||
memory_->CancelAccessWatch(texture->access_watch_handle);
|
memory_->CancelAccessWatch(texture->access_watch_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tell the trace writer to cache this memory but don't read it
|
||||||
|
trace_writer_->WriteMemoryReadCachedNop(texture_info.guest_address,
|
||||||
|
texture_info.input_length);
|
||||||
|
|
||||||
texture->access_watch_handle = memory_->AddPhysicalAccessWatch(
|
texture->access_watch_handle = memory_->AddPhysicalAccessWatch(
|
||||||
texture_info.guest_address, texture_info.input_length,
|
texture_info.guest_address, texture_info.input_length,
|
||||||
cpu::MMIOHandler::kWatchWrite,
|
cpu::MMIOHandler::kWatchWrite,
|
||||||
|
@ -548,6 +560,9 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace_writer_->WriteMemoryRead(texture_info.guest_address,
|
||||||
|
texture_info.input_length);
|
||||||
|
|
||||||
bool uploaded = false;
|
bool uploaded = false;
|
||||||
switch (texture_info.dimension) {
|
switch (texture_info.dimension) {
|
||||||
case Dimension::k1D: {
|
case Dimension::k1D: {
|
||||||
|
@ -1479,9 +1494,6 @@ bool TextureCache::SetupTextureBinding(VkCommandBuffer command_buffer,
|
||||||
uint16_t swizzle = static_cast<uint16_t>(fetch.swizzle);
|
uint16_t swizzle = static_cast<uint16_t>(fetch.swizzle);
|
||||||
auto view = DemandView(texture, swizzle);
|
auto view = DemandView(texture, swizzle);
|
||||||
|
|
||||||
trace_writer_->WriteMemoryRead(texture_info.guest_address,
|
|
||||||
texture_info.input_length);
|
|
||||||
|
|
||||||
auto image_info =
|
auto image_info =
|
||||||
&update_set_info->image_infos[update_set_info->image_write_count];
|
&update_set_info->image_infos[update_set_info->image_write_count];
|
||||||
auto image_write =
|
auto image_write =
|
||||||
|
@ -1507,35 +1519,7 @@ bool TextureCache::SetupTextureBinding(VkCommandBuffer command_buffer,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureCache::ClearCache() {
|
void TextureCache::RemoveInvalidatedTextures() {
|
||||||
// TODO(DrChat): Nuke everything.
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureCache::Scavenge() {
|
|
||||||
// Close any open descriptor pool batches
|
|
||||||
if (descriptor_pool_->has_open_batch()) {
|
|
||||||
descriptor_pool_->EndBatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free unused descriptor sets
|
|
||||||
// TODO(DrChat): These sets could persist across frames, we just need a smart
|
|
||||||
// way to detect if they're unused and free them.
|
|
||||||
texture_bindings_.clear();
|
|
||||||
descriptor_pool_->Scavenge();
|
|
||||||
staging_buffer_.Scavenge();
|
|
||||||
|
|
||||||
// Kill all pending delete textures.
|
|
||||||
if (!pending_delete_textures_.empty()) {
|
|
||||||
for (auto it = pending_delete_textures_.begin();
|
|
||||||
it != pending_delete_textures_.end();) {
|
|
||||||
if (!FreeTexture(*it)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
it = pending_delete_textures_.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean up any invalidated textures.
|
// Clean up any invalidated textures.
|
||||||
invalidated_textures_mutex_.lock();
|
invalidated_textures_mutex_.lock();
|
||||||
std::vector<Texture*>& invalidated_textures = *invalidated_textures_;
|
std::vector<Texture*>& invalidated_textures = *invalidated_textures_;
|
||||||
|
@ -1574,6 +1558,37 @@ void TextureCache::Scavenge() {
|
||||||
invalidated_resolve_textures_mutex_.unlock();
|
invalidated_resolve_textures_mutex_.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TextureCache::ClearCache() {
|
||||||
|
// TODO(DrChat): Nuke everything.
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureCache::Scavenge() {
|
||||||
|
// Close any open descriptor pool batches
|
||||||
|
if (descriptor_pool_->has_open_batch()) {
|
||||||
|
descriptor_pool_->EndBatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free unused descriptor sets
|
||||||
|
// TODO(DrChat): These sets could persist across frames, we just need a smart
|
||||||
|
// way to detect if they're unused and free them.
|
||||||
|
texture_bindings_.clear();
|
||||||
|
descriptor_pool_->Scavenge();
|
||||||
|
staging_buffer_.Scavenge();
|
||||||
|
|
||||||
|
// Kill all pending delete textures.
|
||||||
|
RemoveInvalidatedTextures();
|
||||||
|
if (!pending_delete_textures_.empty()) {
|
||||||
|
for (auto it = pending_delete_textures_.begin();
|
||||||
|
it != pending_delete_textures_.end();) {
|
||||||
|
if (!FreeTexture(*it)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
it = pending_delete_textures_.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace vulkan
|
} // namespace vulkan
|
||||||
} // namespace gpu
|
} // namespace gpu
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -180,6 +180,9 @@ class TextureCache {
|
||||||
UpdateSetInfo* update_set_info,
|
UpdateSetInfo* update_set_info,
|
||||||
const Shader::TextureBinding& binding);
|
const Shader::TextureBinding& binding);
|
||||||
|
|
||||||
|
// Removes invalidated textures from the cache, queues them for delete.
|
||||||
|
void RemoveInvalidatedTextures();
|
||||||
|
|
||||||
Memory* memory_ = nullptr;
|
Memory* memory_ = nullptr;
|
||||||
|
|
||||||
RegisterFile* register_file_ = nullptr;
|
RegisterFile* register_file_ = nullptr;
|
||||||
|
|
Loading…
Reference in New Issue