summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Vulkan/TextureCache.cpp
diff options
context:
space:
mode:
authorStenzek <stenzek@users.noreply.github.com>2017-04-30 00:44:06 +1000
committerGitHub <noreply@github.com>2017-04-30 00:44:06 +1000
commita2cba6d72f049e86460491c875353584e167d613 (patch)
tree91f077a0bb9cd269f73937f843a4922f5812dcc9 /Source/Core/VideoBackends/Vulkan/TextureCache.cpp
parentda880c2a987e6553bec34a7a23ca6aff46bbede7 (diff)
parentcc851c41c11bdc1c192003021df6dc3c2d2b74fb (diff)
Merge pull request #5279 from stenzek/compressed-custom-textures
Native compressed custom texture support
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/TextureCache.cpp')
-rw-r--r--Source/Core/VideoBackends/Vulkan/TextureCache.cpp121
1 files changed, 55 insertions, 66 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp
index 08c370b48f..67faf980ea 100644
--- a/Source/Core/VideoBackends/Vulkan/TextureCache.cpp
+++ b/Source/Core/VideoBackends/Vulkan/TextureCache.cpp
@@ -9,6 +9,7 @@
#include <string>
#include <vector>
+#include "Common/Align.h"
#include "Common/Assert.h"
#include "Common/CommonFuncs.h"
#include "Common/Logging/Log.h"
@@ -238,9 +239,10 @@ TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntry
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
// Allocate texture object
+ VkFormat vk_format = Util::GetVkFormatForHostTextureFormat(config.format);
std::unique_ptr<Texture2D> texture = Texture2D::Create(
- config.width, config.height, config.levels, config.layers, TEXTURECACHE_TEXTURE_FORMAT,
- VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, usage);
+ config.width, config.height, config.levels, config.layers, vk_format, VK_SAMPLE_COUNT_1_BIT,
+ VK_IMAGE_VIEW_TYPE_2D_ARRAY, VK_IMAGE_TILING_OPTIMAL, usage);
if (!texture)
return nullptr;
@@ -341,8 +343,8 @@ TextureCache::TCacheEntry::~TCacheEntry()
g_command_buffer_mgr->DeferFramebufferDestruction(m_framebuffer);
}
-void TextureCache::TCacheEntry::Load(const u8* buffer, unsigned int width, unsigned int height,
- unsigned int expanded_width, unsigned int level)
+void TextureCache::TCacheEntry::Load(u32 level, u32 width, u32 height, u32 row_length,
+ const u8* buffer, size_t buffer_size)
{
// Can't copy data larger than the texture extents.
width = std::max(1u, std::min(width, m_texture->GetWidth() >> level));
@@ -366,87 +368,68 @@ void TextureCache::TCacheEntry::Load(const u8* buffer, unsigned int width, unsig
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
- // Does this texture data fit within the streaming buffer?
- u32 upload_width = width;
- u32 upload_pitch = upload_width * sizeof(u32);
- u32 upload_size = upload_pitch * height;
+ // For unaligned textures, we can save some memory in the transfer buffer by skipping the rows
+ // that lie outside of the texture's dimensions.
u32 upload_alignment = static_cast<u32>(g_vulkan_context->GetBufferImageGranularity());
- u32 source_pitch = expanded_width * 4;
- if ((upload_size + upload_alignment) <= STAGING_TEXTURE_UPLOAD_THRESHOLD &&
- (upload_size + upload_alignment) <= MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE)
- {
- // Assume tightly packed rows, with no padding as the buffer source.
- StreamBuffer* upload_buffer = TextureCache::GetInstance()->m_texture_upload_buffer.get();
+ u32 block_size = Util::GetBlockSize(m_texture->GetFormat());
+ u32 num_rows = Common::AlignUp(height, block_size) / block_size;
+ size_t source_pitch = CalculateHostTextureLevelPitch(config.format, row_length);
+ size_t upload_size = source_pitch * num_rows;
+ std::unique_ptr<StagingBuffer> temp_buffer;
+ VkBuffer upload_buffer;
+ VkDeviceSize upload_buffer_offset;
- // Allocate memory from the streaming buffer for the texture data.
- if (!upload_buffer->ReserveMemory(upload_size, g_vulkan_context->GetBufferImageGranularity()))
+ // Does this texture data fit within the streaming buffer?
+ if (upload_size <= STAGING_TEXTURE_UPLOAD_THRESHOLD &&
+ upload_size <= MAXIMUM_TEXTURE_UPLOAD_BUFFER_SIZE)
+ {
+ StreamBuffer* stream_buffer = TextureCache::GetInstance()->m_texture_upload_buffer.get();
+ if (!stream_buffer->ReserveMemory(upload_size, upload_alignment))
{
// Execute the command buffer first.
WARN_LOG(VIDEO, "Executing command list while waiting for space in texture upload buffer");
Util::ExecuteCurrentCommandsAndRestoreState(false);
// Try allocating again. This may cause a fence wait.
- if (!upload_buffer->ReserveMemory(upload_size, g_vulkan_context->GetBufferImageGranularity()))
+ if (!stream_buffer->ReserveMemory(upload_size, upload_alignment))
PanicAlert("Failed to allocate space in texture upload buffer");
}
- // Grab buffer pointers
- VkBuffer image_upload_buffer = upload_buffer->GetBuffer();
- VkDeviceSize image_upload_buffer_offset = upload_buffer->GetCurrentOffset();
- u8* image_upload_buffer_pointer = upload_buffer->GetCurrentHostPointer();
-
- // Copy to the buffer using the stride from the subresource layout
- const u8* source_ptr = buffer;
- if (upload_pitch != source_pitch)
- {
- VkDeviceSize copy_pitch = std::min(source_pitch, upload_pitch);
- for (unsigned int row = 0; row < height; row++)
- {
- memcpy(image_upload_buffer_pointer + row * upload_pitch, source_ptr + row * source_pitch,
- copy_pitch);
- }
- }
- else
- {
- // Can copy the whole thing in one block, the pitch matches
- memcpy(image_upload_buffer_pointer, source_ptr, upload_size);
- }
-
- // Flush buffer memory if necessary
- upload_buffer->CommitMemory(upload_size);
-
- // Copy from the streaming buffer to the actual image.
- VkBufferImageCopy image_copy = {
- image_upload_buffer_offset, // VkDeviceSize bufferOffset
- 0, // uint32_t bufferRowLength
- 0, // uint32_t bufferImageHeight
- {VK_IMAGE_ASPECT_COLOR_BIT, level, 0, 1}, // VkImageSubresourceLayers imageSubresource
- {0, 0, 0}, // VkOffset3D imageOffset
- {width, height, 1} // VkExtent3D imageExtent
- };
- vkCmdCopyBufferToImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), image_upload_buffer,
- m_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
- &image_copy);
+ // Copy to the streaming buffer.
+ upload_buffer = stream_buffer->GetBuffer();
+ upload_buffer_offset = stream_buffer->GetCurrentOffset();
+ std::memcpy(stream_buffer->GetCurrentHostPointer(), buffer, upload_size);
+ stream_buffer->CommitMemory(upload_size);
}
else
{
- // Slow path. The data for the image is too large to fit in the streaming buffer, so we need
- // to allocate a temporary texture to store the data in, then copy to the real texture.
- std::unique_ptr<StagingTexture2D> staging_texture = StagingTexture2D::Create(
- STAGING_BUFFER_TYPE_UPLOAD, width, height, TEXTURECACHE_TEXTURE_FORMAT);
-
- if (!staging_texture || !staging_texture->Map())
+ // Create a temporary staging buffer that is destroyed after the image is copied.
+ temp_buffer = StagingBuffer::Create(STAGING_BUFFER_TYPE_UPLOAD, upload_size,
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
+ if (!temp_buffer || !temp_buffer->Map())
{
PanicAlert("Failed to allocate staging texture for large texture upload.");
return;
}
- // Copy data to staging texture first, then to the "real" texture.
- staging_texture->WriteTexels(0, 0, width, height, buffer, source_pitch);
- staging_texture->CopyToImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(),
- m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, width,
- height, level, 0);
+ upload_buffer = temp_buffer->GetBuffer();
+ upload_buffer_offset = 0;
+ temp_buffer->Write(0, buffer, upload_size, true);
+ temp_buffer->Unmap();
}
+
+ // Copy from the streaming buffer to the actual image.
+ VkBufferImageCopy image_copy = {
+ upload_buffer_offset, // VkDeviceSize bufferOffset
+ row_length, // uint32_t bufferRowLength
+ 0, // uint32_t bufferImageHeight
+ {VK_IMAGE_ASPECT_COLOR_BIT, level, 0, 1}, // VkImageSubresourceLayers imageSubresource
+ {0, 0, 0}, // VkOffset3D imageOffset
+ {width, height, 1} // VkExtent3D imageExtent
+ };
+ vkCmdCopyBufferToImage(g_command_buffer_mgr->GetCurrentInitCommandBuffer(), upload_buffer,
+ m_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
+ &image_copy);
}
void TextureCache::TCacheEntry::FromRenderTarget(bool is_depth_copy, const EFBRectangle& src_rect,
@@ -544,6 +527,11 @@ bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int l
{
_assert_(level < config.levels);
+ // We can't dump compressed textures currently (it would mean drawing them to a RGBA8
+ // framebuffer, and saving that). TextureCache does not call Save for custom textures
+ // anyway, so this is fine for now.
+ _assert_(config.format == HostTextureFormat::RGBA8);
+
// Determine dimensions of image we want to save.
u32 level_width = std::max(1u, config.width >> level);
u32 level_height = std::max(1u, config.height >> level);
@@ -582,7 +570,8 @@ bool TextureCache::TCacheEntry::Save(const std::string& filename, unsigned int l
// It's okay to throw this texture away immediately, since we're done with it, and
// we blocked until the copy completed on the GPU anyway.
bool result = TextureToPng(reinterpret_cast<u8*>(staging_texture->GetMapPointer()),
- staging_texture->GetRowStride(), filename, level_width, level_height);
+ static_cast<u32>(staging_texture->GetRowStride()), filename,
+ level_width, level_height);
staging_texture->Unmap();
return result;