Skip to content
Permalink
Browse files

Merge pull request #10393 from hrydgard/compute-upload

Vulkan: Texture upload through compute, experimental texture scaling too
  • Loading branch information...
hrydgard committed Oct 8, 2019
2 parents 56e2e08 + be5d93d commit 0b17dd04e6f2be7c7d8d317aa718ee8b1d35c06a
@@ -121,9 +121,10 @@ bool VulkanTexture::CreateDirect(VkCommandBuffer cmd, VulkanDeviceAllocator *all
if (initialLayout != VK_IMAGE_LAYOUT_UNDEFINED && initialLayout != VK_IMAGE_LAYOUT_PREINITIALIZED) {
switch (initialLayout) {
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
case VK_IMAGE_LAYOUT_GENERAL:
TransitionImageLayout2(cmd, image_, 0, numMips, VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_IMAGE_LAYOUT_UNDEFINED, initialLayout,
VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, VK_ACCESS_TRANSFER_WRITE_BIT);
break;
default:
@@ -208,10 +209,10 @@ void VulkanTexture::GenerateMip(VkCommandBuffer cmd, int mip) {
VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_WRITE_BIT);
}

void VulkanTexture::EndCreate(VkCommandBuffer cmd, bool vertexTexture) {
void VulkanTexture::EndCreate(VkCommandBuffer cmd, bool vertexTexture, VkImageLayout layout) {
TransitionImageLayout2(cmd, image_, 0, numMips_,
VK_IMAGE_ASPECT_COLOR_BIT,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
layout, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_PIPELINE_STAGE_TRANSFER_BIT, vertexTexture ? VK_PIPELINE_STAGE_VERTEX_SHADER_BIT : VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
}
@@ -222,6 +223,26 @@ void VulkanTexture::Touch() {
}
}

VkImageView VulkanTexture::CreateViewForMip(int mip) {
VkImageViewCreateInfo view_info = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
view_info.image = image_;
view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
view_info.format = format_;
view_info.components.r = VK_COMPONENT_SWIZZLE_R;
view_info.components.g = VK_COMPONENT_SWIZZLE_G;
view_info.components.b = VK_COMPONENT_SWIZZLE_B;
view_info.components.a = VK_COMPONENT_SWIZZLE_A;
view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
view_info.subresourceRange.baseMipLevel = mip;
view_info.subresourceRange.levelCount = 1;
view_info.subresourceRange.baseArrayLayer = 0;
view_info.subresourceRange.layerCount = 1;
VkImageView view;
VkResult res = vkCreateImageView(vulkan_->GetDevice(), &view_info, NULL, &view);
assert(res == VK_SUCCESS);
return view;
}

void VulkanTexture::Destroy() {
if (view_ != VK_NULL_HANDLE) {
vulkan_->Delete().QueueDeleteImageView(view_);
@@ -21,7 +21,11 @@ class VulkanTexture {
bool CreateDirect(VkCommandBuffer cmd, VulkanDeviceAllocator *allocator, int w, int h, int numMips, VkFormat format, VkImageLayout initialLayout, VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, const VkComponentMapping *mapping = nullptr);
void UploadMip(VkCommandBuffer cmd, int mip, int mipWidth, int mipHeight, VkBuffer buffer, uint32_t offset, size_t rowLength); // rowLength is in pixels
void GenerateMip(VkCommandBuffer cmd, int mip);
void EndCreate(VkCommandBuffer cmd, bool vertexTexture = false);
void EndCreate(VkCommandBuffer cmd, bool vertexTexture = false, VkImageLayout layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);

// When loading mips from compute shaders, you need to pass VK_IMAGE_LAYOUT_GENERAL to the above function.
// In addition, ignore UploadMip and GenerateMip, and instead use GetViewForMip. Make sure to delete the returned views when used.
VkImageView CreateViewForMip(int mip);

void Destroy();

@@ -23,8 +23,8 @@
#include "base/timeutil.h"
#include "math/math_util.h"

VulkanPushBuffer::VulkanPushBuffer(VulkanContext *vulkan, size_t size, VkBufferUsageFlags usage)
: vulkan_(vulkan), size_(size), usage_(usage) {
VulkanPushBuffer::VulkanPushBuffer(VulkanContext *vulkan, size_t size, VkBufferUsageFlags usage, VkMemoryPropertyFlags memoryPropertyMask)
: vulkan_(vulkan), memoryPropertyMask_(memoryPropertyMask), size_(size), usage_(usage) {
bool res = AddBuffer();
assert(res);
}
@@ -58,7 +58,7 @@ bool VulkanPushBuffer::AddBuffer() {
// Okay, that's the buffer. Now let's allocate some memory for it.
VkMemoryAllocateInfo alloc{ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
alloc.allocationSize = reqs.size;
vulkan_->MemoryTypeFromProperties(reqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, &alloc.memoryTypeIndex);
vulkan_->MemoryTypeFromProperties(reqs.memoryTypeBits, memoryPropertyMask_, &alloc.memoryTypeIndex);

res = vkAllocateMemory(device, &alloc, nullptr, &info.deviceMemory);
if (VK_SUCCESS != res) {
@@ -89,7 +89,8 @@ void VulkanPushBuffer::Destroy(VulkanContext *vulkan) {

void VulkanPushBuffer::NextBuffer(size_t minSize) {
// First, unmap the current memory.
Unmap();
if (memoryPropertyMask_ & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
Unmap();

buf_++;
if (buf_ >= buffers_.size() || minSize > size_) {
@@ -108,7 +109,8 @@ void VulkanPushBuffer::NextBuffer(size_t minSize) {

// Now, move to the next buffer and map it.
offset_ = 0;
Map();
if (memoryPropertyMask_ & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
Map();
}

void VulkanPushBuffer::Defragment(VulkanContext *vulkan) {
@@ -142,14 +144,15 @@ void VulkanPushBuffer::Map() {

void VulkanPushBuffer::Unmap() {
_dbg_assert_(G3D, writePtr_ != 0);
/*
// Should not need this since we use coherent memory.
VkMappedMemoryRange range{ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
range.offset = 0;
range.size = offset_;
range.memory = buffers_[buf_].deviceMemory;
vkFlushMappedMemoryRanges(device_, 1, &range);
*/

if ((memoryPropertyMask_ & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) {
VkMappedMemoryRange range{ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
range.offset = 0;
range.size = offset_;
range.memory = buffers_[buf_].deviceMemory;
vkFlushMappedMemoryRanges(vulkan_->GetDevice(), 1, &range);
}

vkUnmapMemory(vulkan_->GetDevice(), buffers_[buf_].deviceMemory);
writePtr_ = nullptr;
}
@@ -22,7 +22,10 @@ class VulkanPushBuffer {
};

public:
VulkanPushBuffer(VulkanContext *vulkan, size_t size, VkBufferUsageFlags usage);
// NOTE: If you create a push buffer with only VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
// then you can't use any of the push functions as pointers will not be reachable from the CPU.
// You must in this case use Allocate() only, and pass the returned offset and the VkBuffer to Vulkan APIs.
VulkanPushBuffer(VulkanContext *vulkan, size_t size, VkBufferUsageFlags usage, VkMemoryPropertyFlags memoryPropertyMask = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
~VulkanPushBuffer();

void Destroy(VulkanContext *vulkan);
@@ -35,15 +38,18 @@ class VulkanPushBuffer {
offset_ = 0;
// Note: we must defrag because some buffers may be smaller than size_.
Defragment(vulkan);
Map();
if (memoryPropertyMask_ & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
Map();
}

void BeginNoReset() {
Map();
if (memoryPropertyMask_ & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
Map();
}

void End() {
Unmap();
if (memoryPropertyMask_ & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
Unmap();
}

void Map();
@@ -109,6 +115,8 @@ class VulkanPushBuffer {
void Defragment(VulkanContext *vulkan);

VulkanContext *vulkan_;
VkMemoryPropertyFlags memoryPropertyMask_;

std::vector<BufInfo> buffers_;
size_t buf_ = 0;
size_t offset_ = 0;
@@ -751,6 +751,7 @@ static ConfigSetting graphicsSettings[] = {
ReportedConfigSetting("TexScalingLevel", &g_Config.iTexScalingLevel, 1, true, true),
ReportedConfigSetting("TexScalingType", &g_Config.iTexScalingType, 0, true, true),
ReportedConfigSetting("TexDeposterize", &g_Config.bTexDeposterize, false, true, true),
ReportedConfigSetting("TexHardwareScaling", &g_Config.bTexHardwareScaling, false, true, true),
ConfigSetting("VSyncInterval", &g_Config.bVSync, false, true, true),
ReportedConfigSetting("BloomHack", &g_Config.iBloomHack, 0, true, true),

@@ -170,6 +170,7 @@ struct Config {
int iTexScalingLevel; // 0 = auto, 1 = off, 2 = 2x, ..., 5 = 5x
int iTexScalingType; // 0 = xBRZ, 1 = Hybrid
bool bTexDeposterize;
bool bTexHardwareScaling;
int iFpsLimit1;
int iFpsLimit2;
int iMaxRecent;
@@ -153,6 +153,8 @@ void DrawEngineVulkan::InitDeviceObjects() {
frame_[i].pushUBO = new VulkanPushBuffer(vulkan_, 8 * 1024 * 1024, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
frame_[i].pushVertex = new VulkanPushBuffer(vulkan_, 2 * 1024 * 1024, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
frame_[i].pushIndex = new VulkanPushBuffer(vulkan_, 1 * 1024 * 1024, VK_BUFFER_USAGE_INDEX_BUFFER_BIT);

frame_[i].pushLocal = new VulkanPushBuffer(vulkan_, 1 * 1024 * 1024, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
}

VkPipelineLayoutCreateInfo pl{ VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
@@ -209,6 +211,11 @@ void DrawEngineVulkan::FrameData::Destroy(VulkanContext *vulkan) {
delete pushIndex;
pushIndex = nullptr;
}
if (pushLocal) {
pushLocal->Destroy(vulkan);
delete pushLocal;
pushLocal = nullptr;
}
}

void DrawEngineVulkan::DestroyDeviceObjects() {
@@ -264,10 +271,12 @@ void DrawEngineVulkan::BeginFrame() {
frame->pushUBO->Reset();
frame->pushVertex->Reset();
frame->pushIndex->Reset();
frame->pushLocal->Reset();

frame->pushUBO->Begin(vulkan_);
frame->pushVertex->Begin(vulkan_);
frame->pushIndex->Begin(vulkan_);
frame->pushLocal->Begin(vulkan_);

// TODO: How can we make this nicer...
tessDataTransferVulkan->SetPushBuffer(frame->pushUBO);
@@ -324,6 +333,7 @@ void DrawEngineVulkan::EndFrame() {
frame->pushUBO->End();
frame->pushVertex->End();
frame->pushIndex->End();
frame->pushLocal->End();
vertexCache_->End();
}

@@ -188,6 +188,11 @@ class DrawEngineVulkan : public DrawEngineCommon {
return frame_[vulkan_->GetCurFrame()].pushUBO;
}

// Only use Allocate on this one.
VulkanPushBuffer *GetPushBufferLocal() {
return frame_[vulkan_->GetCurFrame()].pushLocal;
}

const DrawEngineVulkanStats &GetStats() const {
return stats_;
}
@@ -257,6 +262,10 @@ class DrawEngineVulkan : public DrawEngineCommon {
VulkanPushBuffer *pushUBO = nullptr;
VulkanPushBuffer *pushVertex = nullptr;
VulkanPushBuffer *pushIndex = nullptr;

// Special push buffer in GPU local memory, for texture data conversion and similar tasks.
VulkanPushBuffer *pushLocal;

// We do rolling allocation and reset instead of caching across frames. That we might do later.
DenseHashMap<DescriptorSetKey, VkDescriptorSet, (VkDescriptorSet)VK_NULL_HANDLE> descSets;

0 comments on commit 0b17dd0

Please sign in to comment.
You can’t perform that action at this time.