diff --git a/src/xenia/gpu/vulkan/texture_cache.cc b/src/xenia/gpu/vulkan/texture_cache.cc index 558e796ba..9b7acb89a 100644 --- a/src/xenia/gpu/vulkan/texture_cache.cc +++ b/src/xenia/gpu/vulkan/texture_cache.cc @@ -233,12 +233,13 @@ void TextureCache::Shutdown() { nullptr); } -TextureCache::Texture* TextureCache::AllocateTexture( - const TextureInfo& texture_info, VkFormatFeatureFlags required_flags) { +TextureCache::TextureRegion* TextureCache::AllocateTextureRegion( + Texture* texture, VkOffset3D region_offset, VkExtent3D region_size, + VkFormatFeatureFlags required_flags) { // Create an image first. VkImageCreateInfo image_info = {}; image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - switch (texture_info.dimension) { + switch (texture->texture_info.dimension) { case Dimension::k1D: case Dimension::k2D: image_info.imageType = VK_IMAGE_TYPE_2D; @@ -251,36 +252,28 @@ TextureCache::Texture* TextureCache::AllocateTexture( image_info.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; break; default: - assert_unhandled_case(texture_info.dimension); + assert_unhandled_case(texture->texture_info.dimension); return nullptr; } - assert_not_null(texture_info.format_info()); - auto& config = texture_configs[int(texture_info.format_info()->format)]; - VkFormat format = config.host_format; - assert(format != VK_FORMAT_UNDEFINED); - image_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; // Check the device limits for the format before we create it. VkFormatProperties props; - vkGetPhysicalDeviceFormatProperties(*device_, format, &props); + vkGetPhysicalDeviceFormatProperties(*device_, texture->format, &props); if ((props.optimalTilingFeatures & required_flags) != required_flags) { // Texture needs conversion on upload to a native format. XELOGE( - "Texture Cache: Invalid usage flag specified on format %s (%s)\n\t" - "(requested: %s)", - texture_info.format_info()->name, ui::vulkan::to_string(format), - ui::vulkan::to_flags_string( - static_cast(required_flags & - ~props.optimalTilingFeatures)) - .c_str()); + "Texture Cache: Invalid usage flag specified on format %s (vk %d) " + "(0x%.8X != 0x%.8X)", + texture->texture_info.format_info()->name, texture->format, + (props.optimalTilingFeatures & required_flags), required_flags); assert_always(); } - if (texture_info.dimension != Dimension::kCube && + if (texture->texture_info.dimension != Dimension::kCube && props.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) { // Add color attachment usage if it's supported. image_info.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; @@ -296,43 +289,73 @@ TextureCache::Texture* TextureCache::AllocateTexture( VkImageFormatProperties image_props; vkGetPhysicalDeviceImageFormatProperties( - *device_, format, image_info.imageType, image_info.tiling, + *device_, texture->format, image_info.imageType, image_info.tiling, image_info.usage, image_info.flags, &image_props); // TODO(DrChat): Actually check the image properties. - image_info.format = format; - image_info.extent = {texture_info.width + 1, texture_info.height + 1, 1}; + image_info.format = texture->format; + image_info.extent = region_size; image_info.mipLevels = 1; - image_info.arrayLayers = texture_info.depth + 1; + image_info.arrayLayers = texture->texture_info.depth + 1; image_info.samples = VK_SAMPLE_COUNT_1_BIT; image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; image_info.queueFamilyIndexCount = 0; image_info.pQueueFamilyIndices = nullptr; image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + VkImage image; - VmaAllocation alloc; + VmaAllocation allocation; VmaAllocationCreateInfo vma_create_info = { 0, VMA_MEMORY_USAGE_GPU_ONLY, 0, 0, 0, nullptr, nullptr, }; VmaAllocationInfo vma_info = {}; - VkResult status = vmaCreateImage(mem_allocator_, &image_info, - &vma_create_info, &image, &alloc, &vma_info); + VkResult status = vmaCreateImage(mem_allocator_, &image_info, &vma_reqs, + &image, &allocation, &vma_info); if (status != VK_SUCCESS) { // Allocation failed. return nullptr; } + TextureRegion* region = new TextureRegion(); + region->texture = texture; + + region->region_offset = region_offset; + region->region_size = region_size; + + region->image = image; + region->image_layout = image_info.initialLayout; + region->allocation = allocation; + region->allocation_info = vma_info; + + region->region_contents_valid = false; + + texture->regions.push_back(std::unique_ptr(region)); + return region; +} + +TextureCache::Texture* TextureCache::AllocateTexture( + const TextureInfo& texture_info, VkFormatFeatureFlags required_flags) { + VkOffset3D region_offset = {0, 0, 0}; + VkExtent3D region_extent = {texture_info.width + 1, texture_info.height + 1, + 1}; + auto texture = new Texture(); - texture->format = image_info.format; - texture->image = image; - texture->image_layout = image_info.initialLayout; - texture->alloc = alloc; - texture->alloc_info = vma_info; texture->framebuffer = nullptr; texture->access_watch_handle = 0; texture->texture_info = texture_info; + + assert_not_null(texture->texture_info.format_info()); + auto& config = + texture_configs[int(texture->texture_info.format_info()->format)]; + texture->format = config.host_format != VK_FORMAT_UNDEFINED + ? config.host_format + : VK_FORMAT_R8G8B8A8_UNORM; + + TextureRegion* base_region = AllocateTextureRegion( + texture, region_offset, region_extent, required_flags); + texture->base_region = base_region; return texture; } @@ -349,9 +372,14 @@ bool TextureCache::FreeTexture(Texture* texture) { vkDestroyFramebuffer(*device_, texture->framebuffer, nullptr); } - for (auto it = texture->views.begin(); it != texture->views.end();) { - vkDestroyImageView(*device_, (*it)->view, nullptr); - it = texture->views.erase(it); + for (auto region_it = texture->regions.begin(); + region_it != texture->regions.end(); ++region_it) { + TextureRegion* region = region_it->get(); + for (auto view_it = region->views.begin(); view_it != region->views.end(); + ++view_it) { + vkDestroyImageView(*device_, (*view_it)->view, nullptr); + } + vmaDestroyImage(mem_allocator_, region->image, region->allocation); } if (texture->access_watch_handle) { @@ -359,7 +387,6 @@ bool TextureCache::FreeTexture(Texture* texture) { texture->access_watch_handle = 0; } - vmaDestroyImage(mem_allocator_, texture->image, texture->alloc); delete texture; return true; } @@ -418,7 +445,7 @@ TextureCache::Texture* TextureCache::DemandResolveTexture( // Setup a debug name for the texture. device_->DbgSetObjectName( - reinterpret_cast(texture->image), + reinterpret_cast(texture->base_region->image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, xe::format_string( "0x%.8X - 0x%.8X", texture_info.guest_address, @@ -434,9 +461,9 @@ TextureCache::Texture* TextureCache::DemandResolveTexture( return texture; } -TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, - VkCommandBuffer command_buffer, - VkFence completion_fence) { +TextureCache::TextureRegion* TextureCache::DemandRegion( + const TextureInfo& texture_info, VkCommandBuffer command_buffer, + VkFence completion_fence) { // Run a tight loop to scan for an exact match existing texture. auto texture_hash = texture_info.hash(); for (auto it = textures_.find(texture_hash); it != textures_.end(); ++it) { @@ -450,7 +477,7 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, trace_writer_->WriteMemoryReadCached(texture_info.guest_address, texture_info.input_length); - return it->second; + return it->second->base_region; } } @@ -460,8 +487,102 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, return nullptr; } + // If we didn't find an exact match, see if we can find a subregion of an + // existing texture to use. + VkOffset2D offset; + auto containing_tex = LookupAddress( + texture_info.guest_address, texture_info.width + 1, + texture_info.height + 1, texture_info.format_info()->format, &offset); + if (containing_tex != nullptr) { + // Found a larger texture that contains the requested texels. + // Find/create/update a matching subregion. + + // First, see if the region exists + TextureRegion* region = nullptr; + for (auto region_it = containing_tex->regions.begin(); + region_it != containing_tex->regions.end(); ++region_it) { + const VkOffset3D& roffset = (*region_it)->region_offset; + const VkExtent3D& rsize = (*region_it)->region_size; + if (roffset.x == offset.x && roffset.y == offset.y && + rsize.width == (texture_info.width + 1) && + rsize.height == (texture_info.height + 1)) { + // Match found + region = region_it->get(); + break; + } + } + + if (!region) { + // No match, create a new region and add it the texture + region = AllocateTextureRegion( + containing_tex, {offset.x, offset.y, 0}, + {texture_info.width + 1, texture_info.height + 1, 1}, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); + } + + if (!region->region_contents_valid) { + // Region content is out-of-date, recreate it by blitting from the base + // region. + + // Transition the region into a transfer destination layout. + VkImageMemoryBarrier barrier; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.pNext = nullptr; + barrier.srcAccessMask = 0; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = region->image; + barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; + if (containing_tex->format == VK_FORMAT_D16_UNORM_S8_UINT || + containing_tex->format == VK_FORMAT_D24_UNORM_S8_UINT || + containing_tex->format == VK_FORMAT_D32_SFLOAT_S8_UINT) { + barrier.subresourceRange.aspectMask = + VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + } + + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, + nullptr, 1, &barrier); + + // Now move the converted texture into the destination. + VkImageCopy copy_region; + + copy_region.srcSubresource = {barrier.subresourceRange.aspectMask, 0, 0, + 1}; + copy_region.dstSubresource = {barrier.subresourceRange.aspectMask, 0, 0, + 1}; + copy_region.srcOffset = {offset.x, offset.y, 0}; + copy_region.dstOffset = {0, 0, 0}; + copy_region.extent = {texture_info.width + 1, texture_info.height + 1, 1}; + + vkCmdCopyImage(command_buffer, containing_tex->base_region->image, + containing_tex->base_region->image_layout, region->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); + + // Now transition the region into a shader readonly source. + barrier.srcAccessMask = barrier.dstAccessMask; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.oldLayout = barrier.newLayout; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, + nullptr, 1, &barrier); + + region->image_layout = barrier.newLayout; + + // Mark contents as up-to-date + region->region_contents_valid = true; + } + + return region; + } + // Create a new texture and cache it. - auto texture = AllocateTexture(texture_info); + auto texture = + AllocateTexture(texture_info, VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); if (!texture) { // Failed to allocate texture (out of memory?) assert_always(); @@ -469,18 +590,6 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, return nullptr; } - // Though we didn't find an exact match, that doesn't mean we're out of the - // woods yet. This texture could either be a portion of another texture or - // vice versa. Copy any overlapping textures into this texture. - // TODO: Byte count -> pixel count (on x and y axes) - VkOffset2D offset; - auto collide_tex = LookupAddress( - texture_info.guest_address, texture_info.width + 1, - texture_info.height + 1, texture_info.format_info()->format, &offset); - if (collide_tex != nullptr) { - // assert_always(); - } - trace_writer_->WriteMemoryRead(texture_info.guest_address, texture_info.input_length); @@ -497,7 +606,7 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, // Setup a debug name for the texture. device_->DbgSetObjectName( - reinterpret_cast(texture->image), + reinterpret_cast(texture->base_region->image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, xe::format_string( "0x%.8X - 0x%.8X", texture_info.guest_address, @@ -505,12 +614,12 @@ TextureCache::Texture* TextureCache::Demand(const TextureInfo& texture_info, textures_[texture_hash] = texture; COUNT_profile_set("gpu/texture_cache/textures", textures_.size()); - return texture; + return texture->base_region; } -TextureCache::TextureView* TextureCache::DemandView(Texture* texture, - uint16_t swizzle) { - for (auto it = texture->views.begin(); it != texture->views.end(); ++it) { +TextureCache::TextureRegionView* TextureCache::DemandTextureRegionView( + TextureRegion* region, uint16_t swizzle) { + for (auto it = region->views.begin(); it != region->views.end(); ++it) { if ((*it)->swizzle == swizzle) { return (*it).get(); } @@ -520,10 +629,10 @@ TextureCache::TextureView* TextureCache::DemandView(Texture* texture, view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; view_info.pNext = nullptr; view_info.flags = 0; - view_info.image = texture->image; - view_info.format = texture->format; + view_info.image = region->image; + view_info.format = region->texture->format; - switch (texture->texture_info.dimension) { + switch (region->texture->texture_info.dimension) { case Dimension::k1D: case Dimension::k2D: view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; @@ -552,14 +661,14 @@ TextureCache::TextureView* TextureCache::DemandView(Texture* texture, swiz_component_map[(swizzle >> 9) & 0x7], }; view_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; - if (texture->format == VK_FORMAT_D16_UNORM_S8_UINT || - texture->format == VK_FORMAT_D24_UNORM_S8_UINT || - texture->format == VK_FORMAT_D32_SFLOAT_S8_UINT) { + if (region->texture->format == VK_FORMAT_D16_UNORM_S8_UINT || + region->texture->format == VK_FORMAT_D24_UNORM_S8_UINT || + region->texture->format == VK_FORMAT_D32_SFLOAT_S8_UINT) { // This applies to any depth/stencil format, but we only use D24S8 / D32FS8. view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; } - if (texture->texture_info.dimension == Dimension::kCube) { + if (region->texture->texture_info.dimension == Dimension::kCube) { view_info.subresourceRange.layerCount = 6; } @@ -567,12 +676,12 @@ TextureCache::TextureView* TextureCache::DemandView(Texture* texture, auto status = vkCreateImageView(*device_, &view_info, nullptr, &view); CheckResult(status, "vkCreateImageView"); if (status == VK_SUCCESS) { - auto texture_view = new TextureView(); - texture_view->texture = texture; - texture_view->view = view; - texture_view->swizzle = swizzle; - texture->views.push_back(std::unique_ptr(texture_view)); - return texture_view; + auto region_view = new TextureRegionView(); + region_view->region = region; + region_view->view = view; + region_view->swizzle = swizzle; + region->views.push_back(std::unique_ptr(region_view)); + return region_view; } return nullptr; @@ -1115,7 +1224,8 @@ 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->alloc_info.size, fence); + auto alloc = wb_staging_buffer_.Acquire( + texture->base_region->allocation_info.size, fence); if (!alloc) { wb_command_pool_->EndBatch(); return; @@ -1142,7 +1252,7 @@ void TextureCache::WritebackTexture(Texture* texture) { {texture->texture_info.width + 1, texture->texture_info.height + 1, 1}, }; - vkCmdCopyImageToBuffer(command_buffer, texture->image, + vkCmdCopyImageToBuffer(command_buffer, texture->base_region->image, VK_IMAGE_LAYOUT_GENERAL, wb_staging_buffer_.gpu_buffer(), 1, ®ion); @@ -1235,6 +1345,14 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer, src.guest_address, src.input_length, src.texture_format); } + // Invalidate contents of all regions for this texture, except for the base + // region. + for (auto region_it = dest->regions.begin(); region_it != dest->regions.end(); + ++region_it) { + (*region_it)->region_contents_valid = false; + } + dest->base_region->region_contents_valid = true; + // Upload texture into GPU memory. // TODO: If the GPU supports it, we can submit a compute batch to convert the // texture and copy it to its destination. Otherwise, fallback to conversion @@ -1252,11 +1370,11 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer, barrier.pNext = nullptr; barrier.srcAccessMask = 0; barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - barrier.oldLayout = dest->image_layout; + barrier.oldLayout = dest->base_region->image_layout; barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = dest->image; + barrier.image = dest->base_region->image; barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, copy_region.imageSubresource.baseArrayLayer, copy_region.imageSubresource.layerCount}; @@ -1281,8 +1399,8 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer, copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; } vkCmdCopyBufferToImage(command_buffer, staging_buffer_.gpu_buffer(), - dest->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, - ©_region); + dest->base_region->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); // Now transition the texture into a shader readonly source. barrier.srcAccessMask = barrier.dstAccessMask; @@ -1294,7 +1412,7 @@ bool TextureCache::UploadTexture(VkCommandBuffer command_buffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier); - dest->image_layout = barrier.newLayout; + dest->base_region->image_layout = barrier.newLayout; return true; } @@ -1435,14 +1553,15 @@ bool TextureCache::SetupTextureBinding(VkCommandBuffer command_buffer, // Search via the base format. texture_info.texture_format = GetBaseFormat(texture_info.texture_format); - auto texture = Demand(texture_info, command_buffer, completion_fence); + auto texture_region = + DemandRegion(texture_info, command_buffer, completion_fence); auto sampler = Demand(sampler_info); - if (texture == nullptr || sampler == nullptr) { + if (texture_region == nullptr || sampler == nullptr) { return false; } uint16_t swizzle = static_cast(fetch.swizzle); - auto view = DemandView(texture, swizzle); + auto view = DemandTextureRegionView(texture_region, swizzle); auto image_info = &update_set_info->image_infos[update_set_info->image_write_count]; @@ -1465,9 +1584,9 @@ bool TextureCache::SetupTextureBinding(VkCommandBuffer command_buffer, image_write->pTexelBufferView = nullptr; image_info->imageView = view->view; - image_info->imageLayout = texture->image_layout; + image_info->imageLayout = texture_region->image_layout; image_info->sampler = sampler->sampler; - texture->in_flight_fence = completion_fence; + texture_region->texture->in_flight_fence = completion_fence; return true; } diff --git a/src/xenia/gpu/vulkan/texture_cache.h b/src/xenia/gpu/vulkan/texture_cache.h index af1e10447..1fb7368fb 100644 --- a/src/xenia/gpu/vulkan/texture_cache.h +++ b/src/xenia/gpu/vulkan/texture_cache.h @@ -34,29 +34,11 @@ namespace vulkan { // class TextureCache { public: - struct TextureView; + struct TextureRegion; + struct Texture; - // This represents an uploaded Vulkan texture. - struct Texture { - TextureInfo texture_info; - std::vector> views; - - VkFormat format; - VkImage image; - VkImageLayout image_layout; - VmaAllocation alloc; - VmaAllocationInfo alloc_info; - VkFramebuffer framebuffer; // Blit target frame buffer. - - uintptr_t access_watch_handle; - bool pending_invalidation; - - // Pointer to the latest usage fence. - VkFence in_flight_fence; - }; - - struct TextureView { - Texture* texture; + struct TextureRegionView { + TextureRegion* region; VkImageView view; union { @@ -73,6 +55,43 @@ class TextureCache { }; }; + struct TextureRegion { + Texture* texture; + + std::vector> views; + + VkOffset3D region_offset; + VkExtent3D region_size; + + VkImage image; + VkImageLayout image_layout; + VmaAllocation allocation; + VmaAllocationInfo allocation_info; + + bool region_contents_valid; + }; + + // This represents an uploaded Vulkan texture. A texture has a base image + // region containing its full content area, and zero or more regions + // that are crops of that base region. + struct Texture { + TextureInfo texture_info; + VkFormat format; + + std::vector> regions; + + // Non-owning; base region is also in the (owning) regions vector. + TextureRegion* base_region; + + VkFramebuffer framebuffer; // Blit target frame buffer. + + uintptr_t access_watch_handle; + bool pending_invalidation; + + // Pointer to the latest usage fence. + VkFence in_flight_fence; + }; + TextureCache(Memory* memory, RegisterFile* register_file, TraceWriter* trace_writer, ui::vulkan::VulkanDevice* device); ~TextureCache(); @@ -108,7 +127,7 @@ class TextureCache { uint32_t height, TextureFormat format, VkOffset2D* out_offset = nullptr); - TextureView* DemandView(Texture* texture, uint16_t swizzle); + TextureRegionView* DemandTextureRegionView(TextureRegion*, uint16_t swizzle); // Demands a texture for the purpose of resolving from EDRAM. This either // creates a new texture or returns a previously created texture. @@ -131,18 +150,20 @@ class TextureCache { // Allocates a new texture and memory to back it on the GPU. Texture* AllocateTexture(const TextureInfo& texture_info, - VkFormatFeatureFlags required_flags = - VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT); + VkFormatFeatureFlags required_flags); bool FreeTexture(Texture* texture); static void WatchCallback(void* context_ptr, void* data_ptr, uint32_t address); + TextureRegion* AllocateTextureRegion(Texture*, VkOffset3D region_offset, + VkExtent3D region_size, + VkFormatFeatureFlags required_flags); // Demands a texture. If command_buffer is null and the texture hasn't been // uploaded to graphics memory already, we will return null and bail. - Texture* Demand(const TextureInfo& texture_info, - VkCommandBuffer command_buffer = nullptr, - VkFence completion_fence = nullptr); + TextureRegion* DemandRegion(const TextureInfo& texture_info, + VkCommandBuffer command_buffer = nullptr, + VkFence completion_fence = nullptr); Sampler* Demand(const SamplerInfo& sampler_info); void FlushPendingCommands(VkCommandBuffer command_buffer, diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc index 235aa81ff..5a515cca8 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.cc +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc @@ -436,11 +436,11 @@ void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT; barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - barrier.oldLayout = texture->image_layout; - barrier.newLayout = texture->image_layout; + barrier.oldLayout = texture->base_region->image_layout; + barrier.newLayout = texture->base_region->image_layout; barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = texture->image; + barrier.image = texture->base_region->image; barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; vkCmdPipelineBarrier(copy_commands, @@ -473,7 +473,9 @@ void VulkanCommandProcessor::PerformSwap(uint32_t frontbuffer_ptr, blitter_->BlitTexture2D( copy_commands, current_batch_fence_, - texture_cache_->DemandView(texture, 0x688)->view, src_rect, + texture_cache_->DemandTextureRegionView(texture->base_region, 0x688) + ->view, + src_rect, {texture->texture_info.width + 1, texture->texture_info.height + 1}, VK_FORMAT_R8G8B8A8_UNORM, dst_rect, {frontbuffer_width, frontbuffer_height}, fb_framebuffer_, viewport, @@ -825,7 +827,7 @@ bool VulkanCommandProcessor::PopulateSamplers(VkCommandBuffer command_buffer, std::vector dummy_bindings; auto descriptor_set = texture_cache_->PrepareTextureSet( - setup_buffer, current_batch_fence_, vertex_shader->texture_bindings(), + command_buffer, current_batch_fence_, vertex_shader->texture_bindings(), pixel_shader ? pixel_shader->texture_bindings() : dummy_bindings); if (!descriptor_set) { // Unable to bind set. @@ -1039,7 +1041,15 @@ bool VulkanCommandProcessor::IssueCopy() { } auto command_buffer = current_command_buffer_; - if (texture->image_layout == VK_IMAGE_LAYOUT_UNDEFINED) { + // Mark all regions of the texture (except base region, which we will write + // to) as invalid + for (auto region_it = texture->regions.begin(); + region_it != texture->regions.end(); ++region_it) { + (*region_it)->region_contents_valid = false; + } + texture->base_region->region_contents_valid = true; + + if (texture->base_region->image_layout == VK_IMAGE_LAYOUT_UNDEFINED) { // Transition the image to a general layout. VkImageMemoryBarrier image_barrier; image_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; @@ -1050,13 +1060,13 @@ bool VulkanCommandProcessor::IssueCopy() { image_barrier.dstAccessMask = 0; image_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; image_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - image_barrier.image = texture->image; + image_barrier.image = texture->base_region->image; image_barrier.subresourceRange = {0, 0, 1, 0, 1}; image_barrier.subresourceRange.aspectMask = is_color_source ? VK_IMAGE_ASPECT_COLOR_BIT : VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; - texture->image_layout = VK_IMAGE_LAYOUT_GENERAL; + texture->base_region->image_layout = VK_IMAGE_LAYOUT_GENERAL; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, @@ -1074,11 +1084,11 @@ bool VulkanCommandProcessor::IssueCopy() { image_barrier.dstAccessMask = is_color_source ? VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT : VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; - image_barrier.oldLayout = texture->image_layout; + image_barrier.oldLayout = texture->base_region->image_layout; image_barrier.newLayout = is_color_source ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - image_barrier.image = texture->image; + image_barrier.image = texture->base_region->image; image_barrier.subresourceRange = {0, 0, 1, 0, 1}; image_barrier.subresourceRange.aspectMask = is_color_source ? VK_IMAGE_ASPECT_COLOR_BIT @@ -1152,7 +1162,8 @@ bool VulkanCommandProcessor::IssueCopy() { // Create a framebuffer containing our image. if (!texture->framebuffer) { - auto texture_view = texture_cache_->DemandView(texture, 0x688); + auto texture_view = texture_cache_->DemandTextureRegionView( + texture->base_region, 0x688); VkFramebufferCreateInfo fb_create_info = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,