mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-25 07:09:48 -06:00
Vulkan: Support native compressed textures
This commit is contained in:
@ -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;
|
||||
@ -366,87 +368,68 @@ void TextureCache::TCacheEntry::Load(u32 level, u32 width, u32 height, u32 row_l
|
||||
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 = row_length * 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 = Util::GetPitchForTexture(m_texture->GetFormat(), 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;
|
||||
|
Reference in New Issue
Block a user