diff --git a/src/xenia/gpu/vulkan/texture_cache.cc b/src/xenia/gpu/vulkan/texture_cache.cc index 018f78693..d3b1182e1 100644 --- a/src/xenia/gpu/vulkan/texture_cache.cc +++ b/src/xenia/gpu/vulkan/texture_cache.cc @@ -116,7 +116,9 @@ TextureCache::TextureCache(Memory* memory, RegisterFile* register_file, trace_writer_(trace_writer), device_(device), staging_buffer_(device, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, - kStagingBufferSize) { + kStagingBufferSize), + wb_staging_buffer_(device, VK_BUFFER_USAGE_TRANSFER_DST_BIT, + kStagingBufferSize) { VkResult err = VK_SUCCESS; // Descriptor pool used for all of our cached descriptors. @@ -127,6 +129,9 @@ TextureCache::TextureCache(Memory* memory, RegisterFile* register_file, *device_, 32768, std::vector(pool_sizes, std::end(pool_sizes))); + wb_command_pool_ = std::make_unique( + *device_, VK_QUEUE_FAMILY_IGNORED); + // Check some device limits // On low sampler counts: Rarely would we experience over 16 unique samplers. // This code could be refactored to scale up/down to the # of samplers. @@ -169,6 +174,10 @@ TextureCache::TextureCache(Memory* memory, RegisterFile* register_file, assert_always(); } + if (!wb_staging_buffer_.Initialize()) { + assert_always(); + } + invalidated_textures_sets_[0].reserve(64); invalidated_textures_sets_[1].reserve(64); invalidated_textures_ = &invalidated_textures_sets_[0]; @@ -424,21 +433,8 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, trace_writer_->WriteMemoryRead(texture_info.guest_address, texture_info.input_length); - if (!UploadTexture(command_buffer, completion_fence, texture, texture_info)) { - FreeTexture(texture); - return nullptr; - } - - // Setup a debug name for the texture. - device_->DbgSetObjectName( - reinterpret_cast(texture->image), - VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, - xe::format_string( - "0x%.8X - 0x%.8X", texture_info.guest_address, - texture_info.guest_address + texture_info.input_length)); - - // Okay. Now that the texture is uploaded from system memory, put a writewatch - // on it to tell us if it's been modified from the guest. + // Okay. Put a writewatch on it to tell us if it's been modified from the + // guest. texture->access_watch_handle = memory_->AddPhysicalAccessWatch( texture_info.guest_address, texture_info.input_length, cpu::MMIOHandler::kWatchWrite, @@ -457,6 +453,19 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, }, this, texture); + if (!UploadTexture(command_buffer, completion_fence, texture, texture_info)) { + FreeTexture(texture); + return nullptr; + } + + // Setup a debug name for the texture. + device_->DbgSetObjectName( + reinterpret_cast(texture->image), + VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, + xe::format_string( + "0x%.8X - 0x%.8X", texture_info.guest_address, + texture_info.guest_address + texture_info.input_length)); + textures_[texture_hash] = texture; return texture; } @@ -1115,6 +1124,76 @@ bool TextureCache::ComputeTextureStorage(size_t* output_length, } } +void TextureCache::WritebackTexture(Texture* texture) { + VkResult status = VK_SUCCESS; + VkFence fence = wb_command_pool_->BeginBatch(); + auto alloc = wb_staging_buffer_.Acquire(texture->memory_size, fence); + if (!alloc) { + wb_command_pool_->EndBatch(); + return; + } + + auto command_buffer = wb_command_pool_->AcquireEntry(); + + VkCommandBufferBeginInfo begin_info = { + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr, + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, nullptr, + }; + vkBeginCommandBuffer(command_buffer, &begin_info); + + // TODO: Transition the texture to a transfer source. + + VkBufferImageCopy region = { + alloc->offset, + 0, + 0, + {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + {0, 0, 0}, + {texture->texture_info.width + 1, texture->texture_info.height + 1, 1}, + }; + + vkCmdCopyImageToBuffer(command_buffer, texture->image, + VK_IMAGE_LAYOUT_GENERAL, + wb_staging_buffer_.gpu_buffer(), 1, ®ion); + + // TODO: Transition the texture back to a shader resource. + + vkEndCommandBuffer(command_buffer); + + // Submit the command buffer. + // Submit commands and wait. + { + std::lock_guard(device_->primary_queue_mutex()); + VkSubmitInfo submit_info = { + VK_STRUCTURE_TYPE_SUBMIT_INFO, + nullptr, + 0, + nullptr, + nullptr, + 1, + &command_buffer, + 0, + nullptr, + }; + status = vkQueueSubmit(device_->primary_queue(), 1, &submit_info, fence); + CheckResult(status, "vkQueueSubmit"); + + if (status == VK_SUCCESS) { + status = vkQueueWaitIdle(device_->primary_queue()); + CheckResult(status, "vkQueueWaitIdle"); + } + } + + wb_command_pool_->EndBatch(); + + auto dest = memory_->TranslatePhysical(texture->texture_info.guest_address); + if (status == VK_SUCCESS) { + std::memcpy(dest, alloc->host_ptr, texture->texture_info.input_length); + } + + wb_staging_buffer_.Scavenge(); +} + bool TextureCache::UploadTexture(VkCommandBuffer command_buffer, VkFence completion_fence, Texture* dest, const TextureInfo& src) { diff --git a/src/xenia/gpu/vulkan/texture_cache.h b/src/xenia/gpu/vulkan/texture_cache.h index 241faed88..75ce5cb74 100644 --- a/src/xenia/gpu/vulkan/texture_cache.h +++ b/src/xenia/gpu/vulkan/texture_cache.h @@ -148,6 +148,10 @@ class TextureCache { const TextureInfo& src); bool ComputeTextureStorage(size_t* output_length, const TextureInfo& src); + // Writes a texture back into guest memory. This call is (mostly) asynchronous + // but the texture must not be flagged for destruction. + void WritebackTexture(Texture* texture); + // Queues commands to upload a texture from system memory, applying any // conversions necessary. This may flush the command buffer to the GPU if we // run out of staging memory. @@ -175,11 +179,13 @@ class TextureCache { ui::vulkan::VulkanDevice* device_ = nullptr; VkQueue device_queue_ = nullptr; + std::unique_ptr wb_command_pool_ = nullptr; std::unique_ptr descriptor_pool_ = nullptr; std::unordered_map texture_bindings_; VkDescriptorSetLayout texture_descriptor_set_layout_ = nullptr; ui::vulkan::CircularBuffer staging_buffer_; + ui::vulkan::CircularBuffer wb_staging_buffer_; std::unordered_map textures_; std::unordered_map samplers_; std::list pending_delete_textures_;