[Vulkan] Delayed ImmediateTexture destruction
This commit is contained in:
parent
886129cefa
commit
183269ba16
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
#include "xenia/base/logging.h"
|
#include "xenia/base/logging.h"
|
||||||
|
@ -26,6 +28,12 @@ namespace vulkan {
|
||||||
#include "xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.h"
|
#include "xenia/ui/shaders/bytecode/vulkan_spirv/immediate_frag.h"
|
||||||
#include "xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.h"
|
#include "xenia/ui/shaders/bytecode/vulkan_spirv/immediate_vert.h"
|
||||||
|
|
||||||
|
VulkanImmediateDrawer::VulkanImmediateTexture::~VulkanImmediateTexture() {
|
||||||
|
if (immediate_drawer_) {
|
||||||
|
immediate_drawer_->OnImmediateTextureDestroyed(*this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VulkanImmediateDrawer::VulkanImmediateDrawer(VulkanContext& graphics_context)
|
VulkanImmediateDrawer::VulkanImmediateDrawer(VulkanContext& graphics_context)
|
||||||
: ImmediateDrawer(&graphics_context), context_(graphics_context) {}
|
: ImmediateDrawer(&graphics_context), context_(graphics_context) {}
|
||||||
|
|
||||||
|
@ -64,9 +72,10 @@ bool VulkanImmediateDrawer::Initialize() {
|
||||||
|
|
||||||
// Create the (1, 1, 1, 1) texture as a replacement when drawing without a
|
// Create the (1, 1, 1, 1) texture as a replacement when drawing without a
|
||||||
// real texture.
|
// real texture.
|
||||||
white_texture_index_ = CreateVulkanTexture(
|
size_t white_texture_pending_upload_index;
|
||||||
1, 1, ImmediateTextureFilter::kNearest, false, nullptr);
|
if (!CreateTextureResource(1, 1, ImmediateTextureFilter::kNearest, false,
|
||||||
if (white_texture_index_ == SIZE_MAX) {
|
nullptr, white_texture_,
|
||||||
|
white_texture_pending_upload_index)) {
|
||||||
XELOGE("Failed to create a blank texture for the Vulkan immediate drawer");
|
XELOGE("Failed to create a blank texture for the Vulkan immediate drawer");
|
||||||
Shutdown();
|
Shutdown();
|
||||||
return false;
|
return false;
|
||||||
|
@ -117,39 +126,36 @@ void VulkanImmediateDrawer::Shutdown() {
|
||||||
util::DestroyAndNullHandle(dfn.vkDestroyPipelineLayout, device,
|
util::DestroyAndNullHandle(dfn.vkDestroyPipelineLayout, device,
|
||||||
pipeline_layout_);
|
pipeline_layout_);
|
||||||
|
|
||||||
for (SubmittedTextureUpload& submitted_texture_upload :
|
for (auto& deleted_texture : textures_deleted_) {
|
||||||
texture_uploads_submitted_) {
|
DestroyTextureResource(deleted_texture.first);
|
||||||
if (submitted_texture_upload.buffer != VK_NULL_HANDLE) {
|
|
||||||
dfn.vkDestroyBuffer(device, submitted_texture_upload.buffer, nullptr);
|
|
||||||
}
|
|
||||||
if (submitted_texture_upload.buffer_memory != VK_NULL_HANDLE) {
|
|
||||||
dfn.vkFreeMemory(device, submitted_texture_upload.buffer_memory, nullptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
texture_uploads_submitted_.clear();
|
textures_deleted_.clear();
|
||||||
|
for (SubmittedTextureUploadBuffer& submitted_texture_upload_buffer :
|
||||||
|
texture_upload_buffers_submitted_) {
|
||||||
|
dfn.vkDestroyBuffer(device, submitted_texture_upload_buffer.buffer,
|
||||||
|
nullptr);
|
||||||
|
dfn.vkFreeMemory(device, submitted_texture_upload_buffer.buffer_memory,
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
texture_upload_buffers_submitted_.clear();
|
||||||
for (PendingTextureUpload& pending_texture_upload :
|
for (PendingTextureUpload& pending_texture_upload :
|
||||||
texture_uploads_pending_) {
|
texture_uploads_pending_) {
|
||||||
if (pending_texture_upload.buffer != VK_NULL_HANDLE) {
|
dfn.vkDestroyBuffer(device, pending_texture_upload.buffer, nullptr);
|
||||||
dfn.vkDestroyBuffer(device, pending_texture_upload.buffer, nullptr);
|
dfn.vkFreeMemory(device, pending_texture_upload.buffer_memory, nullptr);
|
||||||
}
|
|
||||||
if (pending_texture_upload.buffer_memory != VK_NULL_HANDLE) {
|
|
||||||
dfn.vkFreeMemory(device, pending_texture_upload.buffer_memory, nullptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
texture_uploads_pending_.clear();
|
texture_uploads_pending_.clear();
|
||||||
textures_free_.clear();
|
for (VulkanImmediateTexture* texture : textures_) {
|
||||||
for (Texture& texture : textures_) {
|
if (texture->immediate_drawer_ != this) {
|
||||||
if (!texture.reference_count) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (texture.immediate_texture) {
|
texture->immediate_drawer_ = nullptr;
|
||||||
texture.immediate_texture->DetachFromImmediateDrawer();
|
DestroyTextureResource(texture->resource_);
|
||||||
}
|
|
||||||
dfn.vkDestroyImageView(device, texture.image_view, nullptr);
|
|
||||||
dfn.vkDestroyImage(device, texture.image, nullptr);
|
|
||||||
dfn.vkFreeMemory(device, texture.memory, nullptr);
|
|
||||||
}
|
}
|
||||||
textures_.clear();
|
textures_.clear();
|
||||||
|
if (white_texture_.image != VK_NULL_HANDLE) {
|
||||||
|
DestroyTextureResource(white_texture_);
|
||||||
|
white_texture_.image = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
texture_descriptor_pool_recycled_first_ = nullptr;
|
texture_descriptor_pool_recycled_first_ = nullptr;
|
||||||
texture_descriptor_pool_unallocated_first_ = nullptr;
|
texture_descriptor_pool_unallocated_first_ = nullptr;
|
||||||
|
@ -166,21 +172,19 @@ std::unique_ptr<ImmediateTexture> VulkanImmediateDrawer::CreateTexture(
|
||||||
uint32_t width, uint32_t height, ImmediateTextureFilter filter,
|
uint32_t width, uint32_t height, ImmediateTextureFilter filter,
|
||||||
bool is_repeated, const uint8_t* data) {
|
bool is_repeated, const uint8_t* data) {
|
||||||
assert_not_null(data);
|
assert_not_null(data);
|
||||||
size_t texture_index =
|
auto texture = std::make_unique<VulkanImmediateTexture>(width, height);
|
||||||
CreateVulkanTexture(width, height, filter, is_repeated, data);
|
size_t pending_upload_index;
|
||||||
if (texture_index == SIZE_MAX) {
|
if (CreateTextureResource(width, height, filter, is_repeated, data,
|
||||||
texture_index = white_texture_index_;
|
texture->resource_, pending_upload_index)) {
|
||||||
|
// Manage by this immediate drawer.
|
||||||
|
texture->immediate_drawer_ = this;
|
||||||
|
texture->immediate_drawer_index_ = textures_.size();
|
||||||
|
textures_.push_back(texture.get());
|
||||||
|
texture->pending_upload_index_ = pending_upload_index;
|
||||||
|
texture_uploads_pending_[texture->pending_upload_index_].texture =
|
||||||
|
texture.get();
|
||||||
}
|
}
|
||||||
Texture& texture = textures_[texture_index];
|
return std::unique_ptr<ImmediateTexture>(texture.release());
|
||||||
auto immediate_texture = std::make_unique<VulkanImmediateTexture>(
|
|
||||||
width, height, this, GetTextureHandleForIndex(texture_index));
|
|
||||||
if (texture_index != white_texture_index_) {
|
|
||||||
texture.immediate_texture = immediate_texture.get();
|
|
||||||
}
|
|
||||||
// Transferring a new reference to a real texture or giving a weak reference
|
|
||||||
// to the white texture (there's no backlink to the ImmediateTexture from it
|
|
||||||
// also).
|
|
||||||
return std::unique_ptr<ImmediateTexture>(immediate_texture.release());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanImmediateDrawer::Begin(int render_target_width,
|
void VulkanImmediateDrawer::Begin(int render_target_width,
|
||||||
|
@ -200,25 +204,30 @@ void VulkanImmediateDrawer::Begin(int render_target_width,
|
||||||
const VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
const VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||||
VkDevice device = provider.device();
|
VkDevice device = provider.device();
|
||||||
|
|
||||||
|
// Destroy deleted textures.
|
||||||
|
for (auto it = textures_deleted_.begin(); it != textures_deleted_.end();) {
|
||||||
|
if (it->second > submission_completed) {
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (std::next(it) != textures_deleted_.end()) {
|
||||||
|
*it = textures_deleted_.back();
|
||||||
|
}
|
||||||
|
textures_deleted_.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
// Release upload buffers for completed texture uploads.
|
// Release upload buffers for completed texture uploads.
|
||||||
auto erase_texture_uploads_end = texture_uploads_submitted_.begin();
|
auto erase_texture_uploads_end = texture_upload_buffers_submitted_.begin();
|
||||||
while (erase_texture_uploads_end != texture_uploads_submitted_.end()) {
|
while (erase_texture_uploads_end != texture_upload_buffers_submitted_.end()) {
|
||||||
if (erase_texture_uploads_end->submission_index > submission_completed) {
|
if (erase_texture_uploads_end->submission_index > submission_completed) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (erase_texture_uploads_end->buffer != VK_NULL_HANDLE) {
|
dfn.vkDestroyBuffer(device, erase_texture_uploads_end->buffer, nullptr);
|
||||||
dfn.vkDestroyBuffer(device, erase_texture_uploads_end->buffer, nullptr);
|
dfn.vkFreeMemory(device, erase_texture_uploads_end->buffer_memory, nullptr);
|
||||||
}
|
|
||||||
if (erase_texture_uploads_end->buffer_memory != VK_NULL_HANDLE) {
|
|
||||||
dfn.vkFreeMemory(device, erase_texture_uploads_end->buffer_memory,
|
|
||||||
nullptr);
|
|
||||||
}
|
|
||||||
// Release the texture reference held for uploading.
|
|
||||||
ReleaseTexture(erase_texture_uploads_end->texture_index);
|
|
||||||
++erase_texture_uploads_end;
|
++erase_texture_uploads_end;
|
||||||
}
|
}
|
||||||
texture_uploads_submitted_.erase(texture_uploads_submitted_.begin(),
|
texture_upload_buffers_submitted_.erase(
|
||||||
erase_texture_uploads_end);
|
texture_upload_buffers_submitted_.begin(), erase_texture_uploads_end);
|
||||||
|
|
||||||
vertex_buffer_pool_->Reclaim(submission_completed);
|
vertex_buffer_pool_->Reclaim(submission_completed);
|
||||||
|
|
||||||
|
@ -352,8 +361,15 @@ void VulkanImmediateDrawer::Draw(const ImmediateDraw& draw) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bind the texture.
|
// Bind the texture.
|
||||||
uint32_t texture_descriptor_index =
|
uint32_t texture_descriptor_index;
|
||||||
textures_[GetTextureIndexForHandle(draw.texture_handle)].descriptor_index;
|
VulkanImmediateTexture* texture =
|
||||||
|
reinterpret_cast<VulkanImmediateTexture*>(draw.texture_handle);
|
||||||
|
if (texture && texture->immediate_drawer_ == this) {
|
||||||
|
texture_descriptor_index = texture->resource_.descriptor_index;
|
||||||
|
texture->last_usage_submission_ = context_.swap_submission_current();
|
||||||
|
} else {
|
||||||
|
texture_descriptor_index = white_texture_.descriptor_index;
|
||||||
|
}
|
||||||
if (current_texture_descriptor_index_ != texture_descriptor_index) {
|
if (current_texture_descriptor_index_ != texture_descriptor_index) {
|
||||||
current_texture_descriptor_index_ = texture_descriptor_index;
|
current_texture_descriptor_index_ = texture_descriptor_index;
|
||||||
VkDescriptorSet texture_descriptor_set =
|
VkDescriptorSet texture_descriptor_set =
|
||||||
|
@ -408,7 +424,7 @@ void VulkanImmediateDrawer::End() {
|
||||||
for (const PendingTextureUpload& pending_texture_upload :
|
for (const PendingTextureUpload& pending_texture_upload :
|
||||||
texture_uploads_pending_) {
|
texture_uploads_pending_) {
|
||||||
image_memory_barriers.emplace_back(image_memory_barrier).image =
|
image_memory_barriers.emplace_back(image_memory_barrier).image =
|
||||||
textures_[pending_texture_upload.texture_index].image;
|
pending_texture_upload.image;
|
||||||
}
|
}
|
||||||
dfn.vkCmdPipelineBarrier(
|
dfn.vkCmdPipelineBarrier(
|
||||||
setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||||
|
@ -416,12 +432,10 @@ void VulkanImmediateDrawer::End() {
|
||||||
uint32_t(image_memory_barriers.size()), image_memory_barriers.data());
|
uint32_t(image_memory_barriers.size()), image_memory_barriers.data());
|
||||||
|
|
||||||
// Do transfer operations and transition to
|
// Do transfer operations and transition to
|
||||||
// VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL.
|
// VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, and also mark as used.
|
||||||
for (size_t i = 0; i < texture_uploads_pending_count; ++i) {
|
for (size_t i = 0; i < texture_uploads_pending_count; ++i) {
|
||||||
const PendingTextureUpload& pending_texture_upload =
|
const PendingTextureUpload& pending_texture_upload =
|
||||||
texture_uploads_pending_[i];
|
texture_uploads_pending_[i];
|
||||||
VkImage texture_upload_image =
|
|
||||||
textures_[pending_texture_upload.texture_index].image;
|
|
||||||
if (pending_texture_upload.buffer != VK_NULL_HANDLE) {
|
if (pending_texture_upload.buffer != VK_NULL_HANDLE) {
|
||||||
// Copying.
|
// Copying.
|
||||||
VkBufferImageCopy copy_region;
|
VkBufferImageCopy copy_region;
|
||||||
|
@ -440,8 +454,16 @@ void VulkanImmediateDrawer::End() {
|
||||||
copy_region.imageExtent.depth = 1;
|
copy_region.imageExtent.depth = 1;
|
||||||
dfn.vkCmdCopyBufferToImage(
|
dfn.vkCmdCopyBufferToImage(
|
||||||
setup_command_buffer, pending_texture_upload.buffer,
|
setup_command_buffer, pending_texture_upload.buffer,
|
||||||
texture_upload_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
|
pending_texture_upload.image,
|
||||||
©_region);
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region);
|
||||||
|
|
||||||
|
SubmittedTextureUploadBuffer& submitted_texture_upload_buffer =
|
||||||
|
texture_upload_buffers_submitted_.emplace_back();
|
||||||
|
submitted_texture_upload_buffer.buffer =
|
||||||
|
pending_texture_upload.buffer;
|
||||||
|
submitted_texture_upload_buffer.buffer_memory =
|
||||||
|
pending_texture_upload.buffer_memory;
|
||||||
|
submitted_texture_upload_buffer.submission_index = submission_current;
|
||||||
} else {
|
} else {
|
||||||
// Clearing (initializing the empty image).
|
// Clearing (initializing the empty image).
|
||||||
VkClearColorValue white_clear_value;
|
VkClearColorValue white_clear_value;
|
||||||
|
@ -449,10 +471,10 @@ void VulkanImmediateDrawer::End() {
|
||||||
white_clear_value.float32[1] = 1.0f;
|
white_clear_value.float32[1] = 1.0f;
|
||||||
white_clear_value.float32[2] = 1.0f;
|
white_clear_value.float32[2] = 1.0f;
|
||||||
white_clear_value.float32[3] = 1.0f;
|
white_clear_value.float32[3] = 1.0f;
|
||||||
dfn.vkCmdClearColorImage(setup_command_buffer, texture_upload_image,
|
dfn.vkCmdClearColorImage(
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
setup_command_buffer, pending_texture_upload.image,
|
||||||
&white_clear_value, 1,
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &white_clear_value, 1,
|
||||||
&image_memory_barrier.subresourceRange);
|
&image_memory_barrier.subresourceRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
VkImageMemoryBarrier& image_memory_barrier_current =
|
VkImageMemoryBarrier& image_memory_barrier_current =
|
||||||
|
@ -465,16 +487,11 @@ void VulkanImmediateDrawer::End() {
|
||||||
image_memory_barrier_current.newLayout =
|
image_memory_barrier_current.newLayout =
|
||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
|
|
||||||
SubmittedTextureUpload& submitted_texture_upload =
|
if (pending_texture_upload.texture) {
|
||||||
texture_uploads_submitted_.emplace_back();
|
pending_texture_upload.texture->last_usage_submission_ =
|
||||||
// Transfer the reference to the texture - need to keep it until the
|
submission_current;
|
||||||
// upload is completed.
|
pending_texture_upload.texture->pending_upload_index_ = SIZE_MAX;
|
||||||
submitted_texture_upload.texture_index =
|
}
|
||||||
pending_texture_upload.texture_index;
|
|
||||||
submitted_texture_upload.buffer = pending_texture_upload.buffer;
|
|
||||||
submitted_texture_upload.buffer_memory =
|
|
||||||
pending_texture_upload.buffer_memory;
|
|
||||||
submitted_texture_upload.submission_index = submission_current;
|
|
||||||
}
|
}
|
||||||
dfn.vkCmdPipelineBarrier(
|
dfn.vkCmdPipelineBarrier(
|
||||||
setup_command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
setup_command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
@ -808,11 +825,11 @@ void VulkanImmediateDrawer::FreeTextureDescriptor(uint32_t descriptor_index) {
|
||||||
pool->recycled_bits |= uint64_t(1) << allocation_index;
|
pool->recycled_bits |= uint64_t(1) << allocation_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t VulkanImmediateDrawer::CreateVulkanTexture(uint32_t width,
|
bool VulkanImmediateDrawer::CreateTextureResource(
|
||||||
uint32_t height,
|
uint32_t width, uint32_t height, ImmediateTextureFilter filter,
|
||||||
ImmediateTextureFilter filter,
|
bool is_repeated, const uint8_t* data,
|
||||||
bool is_repeated,
|
VulkanImmediateTexture::Resource& resource_out,
|
||||||
const uint8_t* data) {
|
size_t& pending_upload_index_out) {
|
||||||
const VulkanProvider& provider = context_.GetVulkanProvider();
|
const VulkanProvider& provider = context_.GetVulkanProvider();
|
||||||
const VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
const VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||||
VkDevice device = provider.device();
|
VkDevice device = provider.device();
|
||||||
|
@ -846,7 +863,7 @@ size_t VulkanImmediateDrawer::CreateVulkanTexture(uint32_t width,
|
||||||
XELOGE(
|
XELOGE(
|
||||||
"Failed to create a Vulkan image for a {}x{} immediate drawer texture",
|
"Failed to create a Vulkan image for a {}x{} immediate drawer texture",
|
||||||
width, height);
|
width, height);
|
||||||
return SIZE_MAX;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkMemoryAllocateInfo image_memory_allocate_info;
|
VkMemoryAllocateInfo image_memory_allocate_info;
|
||||||
|
@ -860,7 +877,7 @@ size_t VulkanImmediateDrawer::CreateVulkanTexture(uint32_t width,
|
||||||
"drawer Vulkan image",
|
"drawer Vulkan image",
|
||||||
width, height);
|
width, height);
|
||||||
dfn.vkDestroyImage(device, image, nullptr);
|
dfn.vkDestroyImage(device, image, nullptr);
|
||||||
return SIZE_MAX;
|
return false;
|
||||||
}
|
}
|
||||||
image_memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
image_memory_allocate_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||||
VkMemoryDedicatedAllocateInfoKHR image_memory_dedicated_allocate_info;
|
VkMemoryDedicatedAllocateInfoKHR image_memory_dedicated_allocate_info;
|
||||||
|
@ -883,14 +900,14 @@ size_t VulkanImmediateDrawer::CreateVulkanTexture(uint32_t width,
|
||||||
"image",
|
"image",
|
||||||
width, height);
|
width, height);
|
||||||
dfn.vkDestroyImage(device, image, nullptr);
|
dfn.vkDestroyImage(device, image, nullptr);
|
||||||
return SIZE_MAX;
|
return false;
|
||||||
}
|
}
|
||||||
if (dfn.vkBindImageMemory(device, image, image_memory, 0) != VK_SUCCESS) {
|
if (dfn.vkBindImageMemory(device, image, image_memory, 0) != VK_SUCCESS) {
|
||||||
XELOGE("Failed to bind memory to a {}x{} immediate drawer Vulkan image",
|
XELOGE("Failed to bind memory to a {}x{} immediate drawer Vulkan image",
|
||||||
width, height);
|
width, height);
|
||||||
dfn.vkDestroyImage(device, image, nullptr);
|
dfn.vkDestroyImage(device, image, nullptr);
|
||||||
dfn.vkFreeMemory(device, image_memory, nullptr);
|
dfn.vkFreeMemory(device, image_memory, nullptr);
|
||||||
return SIZE_MAX;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkImageViewCreateInfo image_view_create_info;
|
VkImageViewCreateInfo image_view_create_info;
|
||||||
|
@ -917,7 +934,7 @@ size_t VulkanImmediateDrawer::CreateVulkanTexture(uint32_t width,
|
||||||
width, height);
|
width, height);
|
||||||
dfn.vkDestroyImage(device, image, nullptr);
|
dfn.vkDestroyImage(device, image, nullptr);
|
||||||
dfn.vkFreeMemory(device, image_memory, nullptr);
|
dfn.vkFreeMemory(device, image_memory, nullptr);
|
||||||
return SIZE_MAX;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t descriptor_index = AllocateTextureDescriptor();
|
uint32_t descriptor_index = AllocateTextureDescriptor();
|
||||||
|
@ -929,7 +946,7 @@ size_t VulkanImmediateDrawer::CreateVulkanTexture(uint32_t width,
|
||||||
dfn.vkDestroyImageView(device, image_view, nullptr);
|
dfn.vkDestroyImageView(device, image_view, nullptr);
|
||||||
dfn.vkDestroyImage(device, image, nullptr);
|
dfn.vkDestroyImage(device, image, nullptr);
|
||||||
dfn.vkFreeMemory(device, image_memory, nullptr);
|
dfn.vkFreeMemory(device, image_memory, nullptr);
|
||||||
return SIZE_MAX;
|
return false;
|
||||||
}
|
}
|
||||||
VkDescriptorImageInfo descriptor_image_info;
|
VkDescriptorImageInfo descriptor_image_info;
|
||||||
VulkanProvider::HostSampler host_sampler;
|
VulkanProvider::HostSampler host_sampler;
|
||||||
|
@ -983,7 +1000,7 @@ size_t VulkanImmediateDrawer::CreateVulkanTexture(uint32_t width,
|
||||||
dfn.vkDestroyImageView(device, image_view, nullptr);
|
dfn.vkDestroyImageView(device, image_view, nullptr);
|
||||||
dfn.vkDestroyImage(device, image, nullptr);
|
dfn.vkDestroyImage(device, image, nullptr);
|
||||||
dfn.vkFreeMemory(device, image_memory, nullptr);
|
dfn.vkFreeMemory(device, image_memory, nullptr);
|
||||||
return SIZE_MAX;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkMemoryAllocateInfo upload_buffer_memory_allocate_info;
|
VkMemoryAllocateInfo upload_buffer_memory_allocate_info;
|
||||||
|
@ -1003,7 +1020,7 @@ size_t VulkanImmediateDrawer::CreateVulkanTexture(uint32_t width,
|
||||||
dfn.vkDestroyImageView(device, image_view, nullptr);
|
dfn.vkDestroyImageView(device, image_view, nullptr);
|
||||||
dfn.vkDestroyImage(device, image, nullptr);
|
dfn.vkDestroyImage(device, image, nullptr);
|
||||||
dfn.vkFreeMemory(device, image_memory, nullptr);
|
dfn.vkFreeMemory(device, image_memory, nullptr);
|
||||||
return SIZE_MAX;
|
return false;
|
||||||
}
|
}
|
||||||
upload_buffer_memory_allocate_info.sType =
|
upload_buffer_memory_allocate_info.sType =
|
||||||
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||||
|
@ -1034,7 +1051,7 @@ size_t VulkanImmediateDrawer::CreateVulkanTexture(uint32_t width,
|
||||||
dfn.vkDestroyImageView(device, image_view, nullptr);
|
dfn.vkDestroyImageView(device, image_view, nullptr);
|
||||||
dfn.vkDestroyImage(device, image, nullptr);
|
dfn.vkDestroyImage(device, image, nullptr);
|
||||||
dfn.vkFreeMemory(device, image_memory, nullptr);
|
dfn.vkFreeMemory(device, image_memory, nullptr);
|
||||||
return SIZE_MAX;
|
return false;
|
||||||
}
|
}
|
||||||
if (dfn.vkBindBufferMemory(device, upload_buffer, upload_buffer_memory,
|
if (dfn.vkBindBufferMemory(device, upload_buffer, upload_buffer_memory,
|
||||||
0) != VK_SUCCESS) {
|
0) != VK_SUCCESS) {
|
||||||
|
@ -1048,7 +1065,7 @@ size_t VulkanImmediateDrawer::CreateVulkanTexture(uint32_t width,
|
||||||
dfn.vkDestroyImageView(device, image_view, nullptr);
|
dfn.vkDestroyImageView(device, image_view, nullptr);
|
||||||
dfn.vkDestroyImage(device, image, nullptr);
|
dfn.vkDestroyImage(device, image, nullptr);
|
||||||
dfn.vkFreeMemory(device, image_memory, nullptr);
|
dfn.vkFreeMemory(device, image_memory, nullptr);
|
||||||
return SIZE_MAX;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* upload_buffer_mapping;
|
void* upload_buffer_mapping;
|
||||||
|
@ -1064,7 +1081,7 @@ size_t VulkanImmediateDrawer::CreateVulkanTexture(uint32_t width,
|
||||||
dfn.vkDestroyImageView(device, image_view, nullptr);
|
dfn.vkDestroyImageView(device, image_view, nullptr);
|
||||||
dfn.vkDestroyImage(device, image, nullptr);
|
dfn.vkDestroyImage(device, image, nullptr);
|
||||||
dfn.vkFreeMemory(device, image_memory, nullptr);
|
dfn.vkFreeMemory(device, image_memory, nullptr);
|
||||||
return SIZE_MAX;
|
return false;
|
||||||
}
|
}
|
||||||
std::memcpy(upload_buffer_mapping, data, data_size);
|
std::memcpy(upload_buffer_mapping, data, data_size);
|
||||||
util::FlushMappedMemoryRange(
|
util::FlushMappedMemoryRange(
|
||||||
|
@ -1073,59 +1090,66 @@ size_t VulkanImmediateDrawer::CreateVulkanTexture(uint32_t width,
|
||||||
dfn.vkUnmapMemory(device, upload_buffer_memory);
|
dfn.vkUnmapMemory(device, upload_buffer_memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t texture_index;
|
resource_out.image = image;
|
||||||
if (!textures_free_.empty()) {
|
resource_out.memory = image_memory;
|
||||||
texture_index = textures_free_.back();
|
resource_out.image_view = image_view;
|
||||||
textures_free_.pop_back();
|
resource_out.descriptor_index = descriptor_index;
|
||||||
} else {
|
|
||||||
texture_index = textures_.size();
|
|
||||||
textures_.emplace_back();
|
|
||||||
}
|
|
||||||
Texture& texture = textures_[texture_index];
|
|
||||||
texture.immediate_texture = nullptr;
|
|
||||||
texture.image = image;
|
|
||||||
texture.memory = image_memory;
|
|
||||||
texture.image_view = image_view;
|
|
||||||
texture.descriptor_index = descriptor_index;
|
|
||||||
// The reference that will be returned to the caller.
|
|
||||||
texture.reference_count = 1;
|
|
||||||
|
|
||||||
PendingTextureUpload& pending_texture_upload =
|
pending_upload_index_out = texture_uploads_pending_.size();
|
||||||
|
PendingTextureUpload& pending_upload =
|
||||||
texture_uploads_pending_.emplace_back();
|
texture_uploads_pending_.emplace_back();
|
||||||
// While the upload has not been yet completed, keep a reference to the
|
// The caller will set the ImmedateTexture pointer if needed.
|
||||||
// texture because its lifetime is not tied to that of the ImmediateTexture
|
pending_upload.texture = nullptr;
|
||||||
// (and thus to context's submissions) now.
|
pending_upload.buffer = upload_buffer;
|
||||||
++texture.reference_count;
|
pending_upload.buffer_memory = upload_buffer_memory;
|
||||||
pending_texture_upload.texture_index = texture_index;
|
pending_upload.image = image;
|
||||||
pending_texture_upload.width = width;
|
pending_upload.width = width;
|
||||||
pending_texture_upload.height = height;
|
pending_upload.height = height;
|
||||||
pending_texture_upload.buffer = upload_buffer;
|
|
||||||
pending_texture_upload.buffer_memory = upload_buffer_memory;
|
|
||||||
|
|
||||||
return texture_index;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanImmediateDrawer::ReleaseTexture(size_t index) {
|
void VulkanImmediateDrawer::DestroyTextureResource(
|
||||||
assert_true(index < textures_.size());
|
VulkanImmediateTexture::Resource& resource) {
|
||||||
Texture& texture = textures_[index];
|
FreeTextureDescriptor(resource.descriptor_index);
|
||||||
assert_not_zero(texture.reference_count);
|
|
||||||
if (--texture.reference_count) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If the texture is attached to a VulkanImmediateTexture, the
|
|
||||||
// VulkanImmediateTexture must hold a reference to it.
|
|
||||||
assert_null(texture.immediate_texture);
|
|
||||||
FreeTextureDescriptor(texture.descriptor_index);
|
|
||||||
const VulkanProvider& provider = context_.GetVulkanProvider();
|
const VulkanProvider& provider = context_.GetVulkanProvider();
|
||||||
const VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
const VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||||
VkDevice device = provider.device();
|
VkDevice device = provider.device();
|
||||||
dfn.vkDestroyImageView(device, texture.image_view, nullptr);
|
dfn.vkDestroyImageView(device, resource.image_view, nullptr);
|
||||||
dfn.vkDestroyImage(device, texture.image, nullptr);
|
dfn.vkDestroyImage(device, resource.image, nullptr);
|
||||||
dfn.vkFreeMemory(device, texture.memory, nullptr);
|
dfn.vkFreeMemory(device, resource.memory, nullptr);
|
||||||
textures_free_.push_back(index);
|
}
|
||||||
// TODO(Triang3l): Track last usage submission because it turns out that
|
|
||||||
// deletion in the ImGui and the profiler actually happens before after
|
void VulkanImmediateDrawer::OnImmediateTextureDestroyed(
|
||||||
// awaiting submission completion.
|
VulkanImmediateTexture& texture) {
|
||||||
|
// Remove from the pending uploads.
|
||||||
|
size_t pending_upload_index = texture.pending_upload_index_;
|
||||||
|
if (pending_upload_index != SIZE_MAX) {
|
||||||
|
if (pending_upload_index + 1 < texture_uploads_pending_.size()) {
|
||||||
|
PendingTextureUpload& pending_upload =
|
||||||
|
texture_uploads_pending_[pending_upload_index];
|
||||||
|
pending_upload = texture_uploads_pending_.back();
|
||||||
|
if (pending_upload.texture) {
|
||||||
|
pending_upload.texture->pending_upload_index_ = pending_upload_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
texture_uploads_pending_.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from the texture list.
|
||||||
|
VulkanImmediateTexture*& texture_at_index =
|
||||||
|
textures_[texture.immediate_drawer_index_];
|
||||||
|
texture_at_index = textures_.back();
|
||||||
|
texture_at_index->immediate_drawer_index_ = texture.immediate_drawer_index_;
|
||||||
|
textures_.pop_back();
|
||||||
|
|
||||||
|
// Destroy immediately or queue for destruction if in use.
|
||||||
|
if (texture.last_usage_submission_ > context_.swap_submission_completed()) {
|
||||||
|
textures_deleted_.emplace_back(
|
||||||
|
std::make_pair(texture.resource_, texture.last_usage_submission_));
|
||||||
|
} else {
|
||||||
|
DestroyTextureResource(texture.resource_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace vulkan
|
} // namespace vulkan
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_set>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "xenia/ui/immediate_drawer.h"
|
#include "xenia/ui/immediate_drawer.h"
|
||||||
|
@ -54,24 +54,28 @@ class VulkanImmediateDrawer : public ImmediateDrawer {
|
||||||
|
|
||||||
class VulkanImmediateTexture : public ImmediateTexture {
|
class VulkanImmediateTexture : public ImmediateTexture {
|
||||||
public:
|
public:
|
||||||
VulkanImmediateTexture(uint32_t width, uint32_t height,
|
struct Resource {
|
||||||
VulkanImmediateDrawer* immediate_drawer,
|
VkImage image;
|
||||||
uintptr_t immediate_drawer_handle)
|
VkDeviceMemory memory;
|
||||||
: ImmediateTexture(width, height), immediate_drawer_(immediate_drawer) {
|
VkImageView image_view;
|
||||||
handle = immediate_drawer_handle;
|
uint32_t descriptor_index;
|
||||||
}
|
};
|
||||||
~VulkanImmediateTexture() {
|
|
||||||
if (immediate_drawer_) {
|
|
||||||
immediate_drawer_->HandleImmediateTextureDestroyed(handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void DetachFromImmediateDrawer() {
|
|
||||||
immediate_drawer_ = nullptr;
|
|
||||||
handle = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
VulkanImmediateTexture(uint32_t width, uint32_t height)
|
||||||
|
: ImmediateTexture(width, height), immediate_drawer_(nullptr) {
|
||||||
|
handle = reinterpret_cast<uintptr_t>(this);
|
||||||
|
}
|
||||||
|
~VulkanImmediateTexture() override;
|
||||||
|
|
||||||
|
// If null, this is either a blank texture, or the immediate drawer has been
|
||||||
|
// destroyed.
|
||||||
VulkanImmediateDrawer* immediate_drawer_;
|
VulkanImmediateDrawer* immediate_drawer_;
|
||||||
|
size_t immediate_drawer_index_;
|
||||||
|
// Invalid if immediate_drawer_ is null, since it's managed by the immediate
|
||||||
|
// drawer.
|
||||||
|
Resource resource_;
|
||||||
|
size_t pending_upload_index_;
|
||||||
|
uint64_t last_usage_submission_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TextureDescriptorPool {
|
struct TextureDescriptorPool {
|
||||||
|
@ -86,19 +90,6 @@ class VulkanImmediateDrawer : public ImmediateDrawer {
|
||||||
TextureDescriptorPool* recycled_next;
|
TextureDescriptorPool* recycled_next;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tracked separately from VulkanImmediateTexture because copying may take
|
|
||||||
// additional references.
|
|
||||||
struct Texture {
|
|
||||||
// Null for the white texture, reference held by the drawer itself instead
|
|
||||||
// of immediate textures.
|
|
||||||
VulkanImmediateTexture* immediate_texture;
|
|
||||||
VkImage image;
|
|
||||||
VkDeviceMemory memory;
|
|
||||||
VkImageView image_view;
|
|
||||||
uint32_t descriptor_index;
|
|
||||||
uint32_t reference_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool EnsurePipelinesCreated();
|
bool EnsurePipelinesCreated();
|
||||||
|
|
||||||
// Allocates a combined image sampler in a pool and returns its index, or
|
// Allocates a combined image sampler in a pool and returns its index, or
|
||||||
|
@ -107,31 +98,15 @@ class VulkanImmediateDrawer : public ImmediateDrawer {
|
||||||
VkDescriptorSet GetTextureDescriptor(uint32_t descriptor_index) const;
|
VkDescriptorSet GetTextureDescriptor(uint32_t descriptor_index) const;
|
||||||
void FreeTextureDescriptor(uint32_t descriptor_index);
|
void FreeTextureDescriptor(uint32_t descriptor_index);
|
||||||
|
|
||||||
// Returns SIZE_MAX in case of failure. The created texture will have a
|
// If data is null, a (1, 1, 1, 1) image will be created, which can be used as
|
||||||
// reference count of 1 plus references needed for uploading, but will not be
|
// a replacement when drawing without a real texture.
|
||||||
// attached to a VulkanImmediateTexture (will return the reference to the
|
bool CreateTextureResource(uint32_t width, uint32_t height,
|
||||||
// caller, in short). If data is null, a (1, 1, 1, 1) image will be created,
|
|
||||||
// which can be used as a replacement when drawing without a real texture.
|
|
||||||
size_t CreateVulkanTexture(uint32_t width, uint32_t height,
|
|
||||||
ImmediateTextureFilter filter, bool is_repeated,
|
ImmediateTextureFilter filter, bool is_repeated,
|
||||||
const uint8_t* data);
|
const uint8_t* data,
|
||||||
void ReleaseTexture(size_t index);
|
VulkanImmediateTexture::Resource& resource_out,
|
||||||
uintptr_t GetTextureHandleForIndex(size_t index) const {
|
size_t& pending_upload_index_out);
|
||||||
return index != white_texture_index_ ? uintptr_t(index + 1) : 0;
|
void DestroyTextureResource(VulkanImmediateTexture::Resource& resource);
|
||||||
}
|
void OnImmediateTextureDestroyed(VulkanImmediateTexture& texture);
|
||||||
size_t GetTextureIndexForHandle(uintptr_t handle) const {
|
|
||||||
// 0 is a special value for no texture.
|
|
||||||
return handle ? size_t(handle - 1) : white_texture_index_;
|
|
||||||
}
|
|
||||||
// For calling from VulkanImmediateTexture.
|
|
||||||
void HandleImmediateTextureDestroyed(uintptr_t handle) {
|
|
||||||
size_t index = GetTextureIndexForHandle(handle);
|
|
||||||
if (index == white_texture_index_) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
textures_[index].immediate_texture = nullptr;
|
|
||||||
ReleaseTexture(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
VulkanContext& context_;
|
VulkanContext& context_;
|
||||||
|
|
||||||
|
@ -141,26 +116,28 @@ class VulkanImmediateDrawer : public ImmediateDrawer {
|
||||||
TextureDescriptorPool* texture_descriptor_pool_unallocated_first_ = nullptr;
|
TextureDescriptorPool* texture_descriptor_pool_unallocated_first_ = nullptr;
|
||||||
TextureDescriptorPool* texture_descriptor_pool_recycled_first_ = nullptr;
|
TextureDescriptorPool* texture_descriptor_pool_recycled_first_ = nullptr;
|
||||||
|
|
||||||
std::vector<Texture> textures_;
|
VulkanImmediateTexture::Resource white_texture_ = {};
|
||||||
std::vector<size_t> textures_free_;
|
std::vector<VulkanImmediateTexture*> textures_;
|
||||||
struct PendingTextureUpload {
|
struct PendingTextureUpload {
|
||||||
size_t texture_index;
|
// Null for internal resources such as the white texture.
|
||||||
uint32_t width;
|
VulkanImmediateTexture* texture;
|
||||||
uint32_t height;
|
|
||||||
// VK_NULL_HANDLE if need to clear rather than to copy.
|
// VK_NULL_HANDLE if need to clear rather than to copy.
|
||||||
VkBuffer buffer;
|
VkBuffer buffer;
|
||||||
VkDeviceMemory buffer_memory;
|
VkDeviceMemory buffer_memory;
|
||||||
|
VkImage image;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
};
|
};
|
||||||
std::vector<PendingTextureUpload> texture_uploads_pending_;
|
std::vector<PendingTextureUpload> texture_uploads_pending_;
|
||||||
struct SubmittedTextureUpload {
|
struct SubmittedTextureUploadBuffer {
|
||||||
size_t texture_index;
|
|
||||||
// VK_NULL_HANDLE if cleared rather than copied.
|
|
||||||
VkBuffer buffer;
|
VkBuffer buffer;
|
||||||
VkDeviceMemory buffer_memory;
|
VkDeviceMemory buffer_memory;
|
||||||
uint64_t submission_index;
|
uint64_t submission_index;
|
||||||
};
|
};
|
||||||
std::deque<SubmittedTextureUpload> texture_uploads_submitted_;
|
std::deque<SubmittedTextureUploadBuffer> texture_upload_buffers_submitted_;
|
||||||
size_t white_texture_index_;
|
// Resource and last usage submission pairs.
|
||||||
|
std::vector<std::pair<VulkanImmediateTexture::Resource, uint64_t>>
|
||||||
|
textures_deleted_;
|
||||||
|
|
||||||
VkPipelineLayout pipeline_layout_ = VK_NULL_HANDLE;
|
VkPipelineLayout pipeline_layout_ = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue