Merge pull request #4462 from stenzek/vulkan-faster-xfb

Vulkan: Cleanup/refactoring, faster RealXFB
This commit is contained in:
Stenzek
2016-12-05 12:15:38 +10:00
committed by GitHub
25 changed files with 1167 additions and 1701 deletions
@@ -3,7 +3,6 @@ set(SRCS
CommandBufferManager.cpp
FramebufferManager.cpp
ObjectCache.cpp
PaletteTextureConverter.cpp
PerfQuery.cpp
RasterFont.cpp
Renderer.cpp
@@ -15,11 +14,11 @@ set(SRCS
SwapChain.cpp
Texture2D.cpp
TextureCache.cpp
TextureEncoder.cpp
TextureConverter.cpp
Util.cpp
VertexFormat.cpp
VertexManager.cpp
VulkanContext.cpp
VulkanContext.cpp
VulkanLoader.cpp
main.cpp
)
+45 -6
View File
@@ -23,13 +23,46 @@ enum STAGING_BUFFER_TYPE
STAGING_BUFFER_TYPE_READBACK
};
// Descriptor sets
enum DESCRIPTOR_SET
// Descriptor set layouts
enum DESCRIPTOR_SET_LAYOUT
{
DESCRIPTOR_SET_UNIFORM_BUFFERS,
DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS,
DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS,
NUM_DESCRIPTOR_SETS
DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS,
DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS,
DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS,
DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS,
NUM_DESCRIPTOR_SET_LAYOUTS
};
// Descriptor set bind points
enum DESCRIPTOR_SET_BIND_POINT
{
DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS,
DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS,
DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER,
NUM_DESCRIPTOR_SET_BIND_POINTS
};
// We use four pipeline layouts:
// - Standard
// - Per-stage UBO (VS/GS/PS, VS constants accessible from PS)
// - 8 combined image samplers (accessible from PS)
// - BBox Enabled
// - Same as standard, plus a single SSBO accessible from PS
// - Push Constant
// - Same as standard, plus 128 bytes of push constants, accessible from all stages.
// - Texture Decoding
// - Same as push constant, plus a single texel buffer accessible from PS.
//
// All four pipeline layout share the first two descriptor sets (uniform buffers, PS samplers).
// The third descriptor set (see bind points above) is used for storage or texel buffers.
//
enum PIPELINE_LAYOUT
{
PIPELINE_LAYOUT_STANDARD,
PIPELINE_LAYOUT_BBOX,
PIPELINE_LAYOUT_PUSH_CONSTANT,
PIPELINE_LAYOUT_TEXTURE_CONVERSION,
NUM_PIPELINE_LAYOUTS
};
// Uniform buffer bindings within the first descriptor set
@@ -74,9 +107,15 @@ constexpr size_t STAGING_TEXTURE_UPLOAD_THRESHOLD = 1024 * 1024 * 4;
constexpr size_t INITIAL_UNIFORM_STREAM_BUFFER_SIZE = 16 * 1024 * 1024;
constexpr size_t MAXIMUM_UNIFORM_STREAM_BUFFER_SIZE = 32 * 1024 * 1024;
// Texel buffer size for palette and texture decoding.
constexpr size_t TEXTURE_CONVERSION_TEXEL_BUFFER_SIZE = 8 * 1024 * 1024;
// Push constant buffer size for utility shaders
constexpr u32 PUSH_CONSTANT_BUFFER_SIZE = 128;
// Minimum number of draw calls per command buffer when attempting to preempt a readback operation.
constexpr u32 MINIMUM_DRAW_CALLS_PER_COMMAND_BUFFER_FOR_READBACK = 10;
// Rasterization state info
union RasterizationState {
BitField<0, 2, VkCullModeFlags> cull_mode;
@@ -18,6 +18,7 @@
#include "VideoBackends/Vulkan/StateTracker.h"
#include "VideoBackends/Vulkan/StreamBuffer.h"
#include "VideoBackends/Vulkan/Texture2D.h"
#include "VideoBackends/Vulkan/TextureConverter.h"
#include "VideoBackends/Vulkan/Util.h"
#include "VideoBackends/Vulkan/VertexFormat.h"
#include "VideoBackends/Vulkan/VulkanContext.h"
@@ -182,24 +183,12 @@ bool FramebufferManager::CreateEFBRenderPass()
VK_ATTACHMENT_STORE_OP_STORE,
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
VK_ATTACHMENT_STORE_OP_DONT_CARE,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL};
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
// Ensure all reads have finished from the resolved texture before overwriting it.
VkSubpassDependency dependencies[] = {
{VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_SHADER_READ_BIT,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_DEPENDENCY_BY_REGION_BIT},
{0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_SHADER_READ_BIT, VK_DEPENDENCY_BY_REGION_BIT}};
subpass_description.pDepthStencilAttachment = nullptr;
pass_info.pAttachments = &resolve_attachment;
pass_info.attachmentCount = 1;
pass_info.dependencyCount = static_cast<u32>(ArraySize(dependencies));
pass_info.pDependencies = dependencies;
res = vkCreateRenderPass(g_vulkan_context->GetDevice(), &pass_info, nullptr,
&m_depth_resolve_render_pass);
@@ -442,8 +431,8 @@ void FramebufferManager::ReinterpretPixelData(int convtype)
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
g_object_cache->GetStandardPipelineLayout(), m_efb_load_render_pass,
g_object_cache->GetScreenQuadVertexShader(),
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
m_efb_load_render_pass, g_object_cache->GetScreenQuadVertexShader(),
g_object_cache->GetScreenQuadGeometryShader(), pixel_shader);
RasterizationState rs_state = Util::GetNoCullRasterizationState();
@@ -511,8 +500,8 @@ Texture2D* FramebufferManager::ResolveEFBDepthTexture(const VkRect2D& region)
// Draw using resolve shader to write the minimum depth of all samples to the resolve texture.
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
g_object_cache->GetStandardPipelineLayout(), m_depth_resolve_render_pass,
g_object_cache->GetScreenQuadVertexShader(),
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
m_depth_resolve_render_pass, g_object_cache->GetScreenQuadVertexShader(),
g_object_cache->GetScreenQuadGeometryShader(), m_ps_depth_resolve);
draw.BeginRenderPass(m_depth_resolve_framebuffer, region);
draw.SetPSSampler(0, m_efb_depth_texture->GetView(), g_object_cache->GetPointSampler());
@@ -695,10 +684,13 @@ bool FramebufferManager::PopulateColorReadbackTexture()
if (m_efb_width != EFB_WIDTH || m_efb_height != EFB_HEIGHT)
{
m_color_copy_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
g_object_cache->GetStandardPipelineLayout(), m_copy_color_render_pass,
g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE,
m_copy_color_shader);
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
m_copy_color_render_pass, g_object_cache->GetScreenQuadVertexShader(),
VK_NULL_HANDLE, m_copy_color_shader);
VkRect2D rect = {{0, 0}, {EFB_WIDTH, EFB_HEIGHT}};
draw.BeginRenderPass(m_color_copy_framebuffer, rect);
@@ -719,7 +711,6 @@ bool FramebufferManager::PopulateColorReadbackTexture()
}
// Use this as a source texture now.
m_color_copy_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
src_texture = m_color_copy_texture.get();
}
@@ -776,10 +767,13 @@ bool FramebufferManager::PopulateDepthReadbackTexture()
}
if (m_efb_width != EFB_WIDTH || m_efb_height != EFB_HEIGHT)
{
m_depth_copy_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
g_object_cache->GetStandardPipelineLayout(), m_copy_depth_render_pass,
g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE,
m_copy_depth_shader);
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
m_copy_depth_render_pass, g_object_cache->GetScreenQuadVertexShader(),
VK_NULL_HANDLE, m_copy_depth_shader);
VkRect2D rect = {{0, 0}, {EFB_WIDTH, EFB_HEIGHT}};
draw.BeginRenderPass(m_depth_copy_framebuffer, rect);
@@ -800,7 +794,6 @@ bool FramebufferManager::PopulateDepthReadbackTexture()
}
// Use this as a source texture now.
m_depth_copy_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
src_texture = m_depth_copy_texture.get();
src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
}
@@ -839,15 +832,15 @@ void FramebufferManager::InvalidatePeekCache()
bool FramebufferManager::CreateReadbackRenderPasses()
{
VkAttachmentDescription copy_attachment = {
0, // VkAttachmentDescriptionFlags flags
EFB_COLOR_TEXTURE_FORMAT, // VkFormat format
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp loadOp
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp
VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout initialLayout
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL // VkImageLayout finalLayout
0, // VkAttachmentDescriptionFlags flags
EFB_COLOR_TEXTURE_FORMAT, // VkFormat format
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp loadOp
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp
VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout initialLayout
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout finalLayout
};
VkAttachmentReference copy_attachment_ref = {
0, // uint32_t attachment
@@ -865,14 +858,6 @@ bool FramebufferManager::CreateReadbackRenderPasses()
0, // uint32_t preserveAttachmentCount
nullptr // const uint32_t* pPreserveAttachments
};
VkSubpassDependency copy_dependency = {
0,
VK_SUBPASS_EXTERNAL,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
VK_ACCESS_TRANSFER_READ_BIT,
VK_DEPENDENCY_BY_REGION_BIT};
VkRenderPassCreateInfo copy_pass = {
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType
nullptr, // const void* pNext
@@ -881,8 +866,8 @@ bool FramebufferManager::CreateReadbackRenderPasses()
&copy_attachment, // const VkAttachmentDescription* pAttachments
1, // uint32_t subpassCount
&copy_subpass, // const VkSubpassDescription* pSubpasses
1, // uint32_t dependencyCount
&copy_dependency // const VkSubpassDependency* pDependencies
0, // uint32_t dependencyCount
nullptr // const VkSubpassDependency* pDependencies
};
VkResult res = vkCreateRenderPass(g_vulkan_context->GetDevice(), &copy_pass, nullptr,
@@ -1007,10 +992,8 @@ bool FramebufferManager::CreateReadbackTextures()
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
// We can't copy to/from color<->depth formats, so using a linear texture is not an option here.
// TODO: Investigate if vkCmdBlitImage can be used. The documentation isn't that clear.
m_depth_readback_texture = StagingTexture2DBuffer::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH,
EFB_HEIGHT, EFB_DEPTH_TEXTURE_FORMAT);
m_depth_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH,
EFB_HEIGHT, EFB_DEPTH_TEXTURE_FORMAT);
if (!m_depth_copy_texture || !m_depth_readback_texture)
{
ERROR_LOG(VIDEO, "Failed to create EFB depth readback texture");
@@ -1024,12 +1007,6 @@ bool FramebufferManager::CreateReadbackTextures()
return false;
}
// Transition to TRANSFER_SRC, as this is expected by the render pass.
m_color_copy_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
m_depth_copy_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(),
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
return true;
}
@@ -1171,7 +1148,7 @@ void FramebufferManager::DrawPokeVertices(const EFBPokeVertex* vertices, size_t
// We don't use the utility shader in order to keep the vertices compact.
PipelineInfo pipeline_info = {};
pipeline_info.vertex_format = m_poke_vertex_format.get();
pipeline_info.pipeline_layout = g_object_cache->GetStandardPipelineLayout();
pipeline_info.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
pipeline_info.vs = m_poke_vertex_shader;
pipeline_info.gs = (m_efb_layers > 1) ? m_poke_geometry_shader : VK_NULL_HANDLE;
pipeline_info.ps = m_poke_fragment_shader;
@@ -1413,7 +1390,7 @@ void FramebufferManager::CopyToRealXFB(u32 xfb_addr, u32 fb_stride, u32 fb_heigh
// The destination stride can differ from the copy region width, in which case the pixels
// outside the copy region should not be written to.
TextureCache::GetInstance()->EncodeYUYVTextureToMemory(
TextureCache::GetInstance()->GetTextureConverter()->EncodeTextureToMemoryYUYV(
xfb_ptr, static_cast<u32>(source_rc.GetWidth()), fb_stride, fb_height, src_texture,
scaled_rc);
@@ -1439,8 +1416,8 @@ void XFBSource::DecodeToTexture(u32 xfb_addr, u32 fb_width, u32 fb_height)
// Guest memory -> GPU EFB Textures
const u8* src_ptr = Memory::GetPointer(xfb_addr);
_assert_(src_ptr);
TextureCache::GetInstance()->DecodeYUYVTextureFromMemory(m_texture.get(), src_ptr, fb_width,
fb_width * 2, fb_height);
TextureCache::GetInstance()->GetTextureConverter()->DecodeYUYVTextureFromMemory(
m_texture.get(), src_ptr, fb_width, fb_width * 2, fb_height);
}
void XFBSource::CopyEFB(float gamma)
@@ -695,15 +695,21 @@ bool ObjectCache::CreateDescriptorSetLayouts()
static const VkDescriptorSetLayoutBinding ssbo_set_bindings[] = {
{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}};
static const VkDescriptorSetLayoutCreateInfo create_infos[NUM_DESCRIPTOR_SETS] = {
static const VkDescriptorSetLayoutBinding texel_buffer_set_bindings[] = {
{0, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
};
static const VkDescriptorSetLayoutCreateInfo create_infos[NUM_DESCRIPTOR_SET_LAYOUTS] = {
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(ubo_set_bindings)), ubo_set_bindings},
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(sampler_set_bindings)), sampler_set_bindings},
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(ssbo_set_bindings)), ssbo_set_bindings}};
static_cast<u32>(ArraySize(ssbo_set_bindings)), ssbo_set_bindings},
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(texel_buffer_set_bindings)), texel_buffer_set_bindings}};
for (size_t i = 0; i < NUM_DESCRIPTOR_SETS; i++)
for (size_t i = 0; i < NUM_DESCRIPTOR_SET_LAYOUTS; i++)
{
VkResult res = vkCreateDescriptorSetLayout(g_vulkan_context->GetDevice(), &create_infos[i],
nullptr, &m_descriptor_set_layouts[i]);
@@ -732,47 +738,46 @@ bool ObjectCache::CreatePipelineLayouts()
// Descriptor sets for each pipeline layout
VkDescriptorSetLayout standard_sets[] = {
m_descriptor_set_layouts[DESCRIPTOR_SET_UNIFORM_BUFFERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS]};
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS]};
VkDescriptorSetLayout bbox_sets[] = {
m_descriptor_set_layouts[DESCRIPTOR_SET_UNIFORM_BUFFERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS]};
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS]};
VkDescriptorSetLayout texture_conversion_sets[] = {
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS],
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS]};
VkPushConstantRange push_constant_range = {
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, PUSH_CONSTANT_BUFFER_SIZE};
// Info for each pipeline layout
VkPipelineLayoutCreateInfo standard_info = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
nullptr,
0,
static_cast<u32>(ArraySize(standard_sets)),
standard_sets,
0,
nullptr};
VkPipelineLayoutCreateInfo bbox_info = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
nullptr,
0,
static_cast<u32>(ArraySize(bbox_sets)),
bbox_sets,
0,
nullptr};
VkPipelineLayoutCreateInfo push_constant_info = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
nullptr,
0,
static_cast<u32>(ArraySize(standard_sets)),
standard_sets,
1,
&push_constant_range};
VkPipelineLayoutCreateInfo pipeline_layout_info[NUM_PIPELINE_LAYOUTS] = {
// Standard
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(standard_sets)), standard_sets, 0, nullptr},
if ((res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &standard_info, nullptr,
&m_standard_pipeline_layout)) != VK_SUCCESS ||
(res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &bbox_info, nullptr,
&m_bbox_pipeline_layout)) != VK_SUCCESS ||
(res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &push_constant_info, nullptr,
&m_push_constant_pipeline_layout)))
// BBox
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(bbox_sets)), bbox_sets, 0, nullptr},
// Push Constant
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(standard_sets)), standard_sets, 1, &push_constant_range},
// Texture Conversion
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(texture_conversion_sets)), texture_conversion_sets, 1,
&push_constant_range}};
for (size_t i = 0; i < NUM_PIPELINE_LAYOUTS; i++)
{
LOG_VULKAN_ERROR(res, "vkCreatePipelineLayout failed: ");
return false;
if ((res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &pipeline_layout_info[i],
nullptr, &m_pipeline_layouts[i])) != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreatePipelineLayout failed: ");
return false;
}
}
return true;
@@ -780,13 +785,11 @@ bool ObjectCache::CreatePipelineLayouts()
void ObjectCache::DestroyPipelineLayouts()
{
if (m_standard_pipeline_layout != VK_NULL_HANDLE)
vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_standard_pipeline_layout, nullptr);
if (m_bbox_pipeline_layout != VK_NULL_HANDLE)
vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_bbox_pipeline_layout, nullptr);
if (m_push_constant_pipeline_layout != VK_NULL_HANDLE)
vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_push_constant_pipeline_layout,
nullptr);
for (VkPipelineLayout layout : m_pipeline_layouts)
{
if (layout != VK_NULL_HANDLE)
vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), layout, nullptr);
}
}
bool ObjectCache::CreateUtilityShaderVertexFormat()
+10 -22
View File
@@ -62,25 +62,16 @@ public:
ObjectCache();
~ObjectCache();
// We have four shared pipeline layouts:
// - Standard
// - Per-stage UBO (VS/GS/PS, VS constants accessible from PS)
// - 8 combined image samplers (accessible from PS)
// - BBox Enabled
// - Same as standard, plus a single SSBO accessible from PS
// - Push Constant
// - Same as standard, plus 128 bytes of push constants, accessible from all stages.
//
// All three pipeline layouts use the same descriptor set layouts, but the final descriptor set
// (SSBO) is only required when using the BBox Enabled pipeline layout.
//
VkDescriptorSetLayout GetDescriptorSetLayout(DESCRIPTOR_SET set) const
// Descriptor set layout accessor. Used for allocating descriptor sets.
VkDescriptorSetLayout GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT layout) const
{
return m_descriptor_set_layouts[set];
return m_descriptor_set_layouts[layout];
}
// Pipeline layout accessor. Used to fill in required field in PipelineInfo.
VkPipelineLayout GetPipelineLayout(PIPELINE_LAYOUT layout) const
{
return m_pipeline_layouts[layout];
}
VkPipelineLayout GetStandardPipelineLayout() const { return m_standard_pipeline_layout; }
VkPipelineLayout GetBBoxPipelineLayout() const { return m_bbox_pipeline_layout; }
VkPipelineLayout GetPushConstantPipelineLayout() const { return m_push_constant_pipeline_layout; }
// Shared utility shader resources
VertexFormat* GetUtilityShaderVertexFormat() const
{
@@ -157,11 +148,8 @@ private:
void DestroySharedShaders();
void DestroySamplers();
std::array<VkDescriptorSetLayout, NUM_DESCRIPTOR_SETS> m_descriptor_set_layouts = {};
VkPipelineLayout m_standard_pipeline_layout = VK_NULL_HANDLE;
VkPipelineLayout m_bbox_pipeline_layout = VK_NULL_HANDLE;
VkPipelineLayout m_push_constant_pipeline_layout = VK_NULL_HANDLE;
std::array<VkDescriptorSetLayout, NUM_DESCRIPTOR_SET_LAYOUTS> m_descriptor_set_layouts = {};
std::array<VkPipelineLayout, NUM_PIPELINE_LAYOUTS> m_pipeline_layouts = {};
std::unique_ptr<VertexFormat> m_utility_shader_vertex_format;
std::unique_ptr<StreamBuffer> m_utility_shader_vertex_buffer;
@@ -1,319 +0,0 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "VideoBackends/Vulkan/PaletteTextureConverter.h"
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <string>
#include "Common/Assert.h"
#include "Common/CommonFuncs.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "VideoBackends/Vulkan/CommandBufferManager.h"
#include "VideoBackends/Vulkan/FramebufferManager.h"
#include "VideoBackends/Vulkan/ObjectCache.h"
#include "VideoBackends/Vulkan/Renderer.h"
#include "VideoBackends/Vulkan/StateTracker.h"
#include "VideoBackends/Vulkan/StreamBuffer.h"
#include "VideoBackends/Vulkan/Texture2D.h"
#include "VideoBackends/Vulkan/Util.h"
#include "VideoBackends/Vulkan/VulkanContext.h"
namespace Vulkan
{
PaletteTextureConverter::PaletteTextureConverter()
{
}
PaletteTextureConverter::~PaletteTextureConverter()
{
for (const auto& it : m_shaders)
{
if (it != VK_NULL_HANDLE)
vkDestroyShaderModule(g_vulkan_context->GetDevice(), it, nullptr);
}
if (m_palette_buffer_view != VK_NULL_HANDLE)
vkDestroyBufferView(g_vulkan_context->GetDevice(), m_palette_buffer_view, nullptr);
if (m_pipeline_layout != VK_NULL_HANDLE)
vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_pipeline_layout, nullptr);
if (m_palette_set_layout != VK_NULL_HANDLE)
vkDestroyDescriptorSetLayout(g_vulkan_context->GetDevice(), m_palette_set_layout, nullptr);
}
bool PaletteTextureConverter::Initialize()
{
if (!CreateBuffers())
return false;
if (!CompileShaders())
return false;
if (!CreateDescriptorLayout())
return false;
return true;
}
void PaletteTextureConverter::ConvertTexture(VkCommandBuffer command_buffer,
VkRenderPass render_pass,
VkFramebuffer dst_framebuffer, Texture2D* src_texture,
u32 width, u32 height, void* palette,
TlutFormat format, u32 src_format)
{
struct PSUniformBlock
{
float multiplier;
int texel_buffer_offset;
int pad[2];
};
_assert_(static_cast<size_t>(format) < NUM_PALETTE_CONVERSION_SHADERS);
size_t palette_size = (src_format & 0xF) == GX_TF_I4 ? 32 : 512;
VkDescriptorSet texel_buffer_descriptor_set;
// Allocate memory for the palette, and descriptor sets for the buffer.
// If any of these fail, execute a command buffer, and try again.
if (!m_palette_stream_buffer->ReserveMemory(palette_size,
g_vulkan_context->GetTexelBufferAlignment()) ||
(texel_buffer_descriptor_set =
g_command_buffer_mgr->AllocateDescriptorSet(m_palette_set_layout)) == VK_NULL_HANDLE)
{
WARN_LOG(VIDEO, "Executing command list while waiting for space in palette buffer");
Util::ExecuteCurrentCommandsAndRestoreState(false);
if (!m_palette_stream_buffer->ReserveMemory(palette_size,
g_vulkan_context->GetTexelBufferAlignment()) ||
(texel_buffer_descriptor_set =
g_command_buffer_mgr->AllocateDescriptorSet(m_palette_set_layout)) == VK_NULL_HANDLE)
{
PanicAlert("Failed to allocate space for texture conversion");
return;
}
}
// Fill descriptor set #2 (texel buffer)
u32 palette_offset = static_cast<u32>(m_palette_stream_buffer->GetCurrentOffset());
VkWriteDescriptorSet texel_set_write = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
nullptr,
texel_buffer_descriptor_set,
0,
0,
1,
VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
nullptr,
nullptr,
&m_palette_buffer_view};
vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), 1, &texel_set_write, 0, nullptr);
Util::BufferMemoryBarrier(command_buffer, m_palette_stream_buffer->GetBuffer(),
VK_ACCESS_HOST_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, palette_offset,
palette_size, VK_PIPELINE_STAGE_HOST_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
// Set up draw
UtilityShaderDraw draw(command_buffer, m_pipeline_layout, render_pass,
g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE,
m_shaders[format]);
VkRect2D region = {{0, 0}, {width, height}};
draw.BeginRenderPass(dst_framebuffer, region);
// Copy in palette
memcpy(m_palette_stream_buffer->GetCurrentHostPointer(), palette, palette_size);
m_palette_stream_buffer->CommitMemory(palette_size);
// PS Uniforms/Samplers
PSUniformBlock uniforms = {};
uniforms.multiplier = (src_format & 0xF) == GX_TF_I4 ? 15.0f : 255.0f;
uniforms.texel_buffer_offset = static_cast<int>(palette_offset / sizeof(u16));
draw.SetPushConstants(&uniforms, sizeof(uniforms));
draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetPointSampler());
// We have to bind the texel buffer descriptor set separately.
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_layout, 0, 1,
&texel_buffer_descriptor_set, 0, nullptr);
// Draw
draw.SetViewportAndScissor(0, 0, width, height);
draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4);
draw.EndRenderPass();
}
bool PaletteTextureConverter::CreateBuffers()
{
// TODO: Check against maximum size
static const size_t BUFFER_SIZE = 1024 * 1024;
m_palette_stream_buffer =
StreamBuffer::Create(VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT, BUFFER_SIZE, BUFFER_SIZE);
if (!m_palette_stream_buffer)
return false;
// Create a view of the whole buffer, we'll offset our texel load into it
VkBufferViewCreateInfo view_info = {
VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO, // VkStructureType sType
nullptr, // const void* pNext
0, // VkBufferViewCreateFlags flags
m_palette_stream_buffer->GetBuffer(), // VkBuffer buffer
VK_FORMAT_R16_UINT, // VkFormat format
0, // VkDeviceSize offset
BUFFER_SIZE // VkDeviceSize range
};
VkResult res = vkCreateBufferView(g_vulkan_context->GetDevice(), &view_info, nullptr,
&m_palette_buffer_view);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateBufferView failed: ");
return false;
}
return true;
}
bool PaletteTextureConverter::CompileShaders()
{
static const char PALETTE_CONVERSION_FRAGMENT_SHADER_SOURCE[] = R"(
layout(std140, push_constant) uniform PCBlock
{
float multiplier;
int texture_buffer_offset;
} PC;
layout(set = 1, binding = 0) uniform sampler2DArray samp0;
layout(set = 0, binding = 0) uniform usamplerBuffer samp1;
layout(location = 0) in vec3 f_uv0;
layout(location = 0) out vec4 ocol0;
int Convert3To8(int v)
{
// Swizzle bits: 00000123 -> 12312312
return (v << 5) | (v << 2) | (v >> 1);
}
int Convert4To8(int v)
{
// Swizzle bits: 00001234 -> 12341234
return (v << 4) | v;
}
int Convert5To8(int v)
{
// Swizzle bits: 00012345 -> 12345123
return (v << 3) | (v >> 2);
}
int Convert6To8(int v)
{
// Swizzle bits: 00123456 -> 12345612
return (v << 2) | (v >> 4);
}
float4 DecodePixel_RGB5A3(int val)
{
int r,g,b,a;
if ((val&0x8000) > 0)
{
r=Convert5To8((val>>10) & 0x1f);
g=Convert5To8((val>>5 ) & 0x1f);
b=Convert5To8((val ) & 0x1f);
a=0xFF;
}
else
{
a=Convert3To8((val>>12) & 0x7);
r=Convert4To8((val>>8 ) & 0xf);
g=Convert4To8((val>>4 ) & 0xf);
b=Convert4To8((val ) & 0xf);
}
return float4(r, g, b, a) / 255.0;
}
float4 DecodePixel_RGB565(int val)
{
int r, g, b, a;
r = Convert5To8((val >> 11) & 0x1f);
g = Convert6To8((val >> 5) & 0x3f);
b = Convert5To8((val) & 0x1f);
a = 0xFF;
return float4(r, g, b, a) / 255.0;
}
float4 DecodePixel_IA8(int val)
{
int i = val & 0xFF;
int a = val >> 8;
return float4(i, i, i, a) / 255.0;
}
void main()
{
int src = int(round(texture(samp0, f_uv0).r * PC.multiplier));
src = int(texelFetch(samp1, src + PC.texture_buffer_offset).r);
src = ((src << 8) & 0xFF00) | (src >> 8);
ocol0 = DECODE(src);
}
)";
std::string palette_ia8_program = StringFromFormat("%s\n%s", "#define DECODE DecodePixel_IA8",
PALETTE_CONVERSION_FRAGMENT_SHADER_SOURCE);
std::string palette_rgb565_program = StringFromFormat(
"%s\n%s", "#define DECODE DecodePixel_RGB565", PALETTE_CONVERSION_FRAGMENT_SHADER_SOURCE);
std::string palette_rgb5a3_program = StringFromFormat(
"%s\n%s", "#define DECODE DecodePixel_RGB5A3", PALETTE_CONVERSION_FRAGMENT_SHADER_SOURCE);
m_shaders[GX_TL_IA8] = Util::CompileAndCreateFragmentShader(palette_ia8_program);
m_shaders[GX_TL_RGB565] = Util::CompileAndCreateFragmentShader(palette_rgb565_program);
m_shaders[GX_TL_RGB5A3] = Util::CompileAndCreateFragmentShader(palette_rgb5a3_program);
return (m_shaders[GX_TL_IA8] != VK_NULL_HANDLE && m_shaders[GX_TL_RGB565] != VK_NULL_HANDLE &&
m_shaders[GX_TL_RGB5A3] != VK_NULL_HANDLE);
}
bool PaletteTextureConverter::CreateDescriptorLayout()
{
static const VkDescriptorSetLayoutBinding set_bindings[] = {
{0, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
};
static const VkDescriptorSetLayoutCreateInfo set_info = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
static_cast<u32>(ArraySize(set_bindings)), set_bindings};
VkResult res = vkCreateDescriptorSetLayout(g_vulkan_context->GetDevice(), &set_info, nullptr,
&m_palette_set_layout);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateDescriptorSetLayout failed: ");
return false;
}
VkDescriptorSetLayout sets[] = {m_palette_set_layout, g_object_cache->GetDescriptorSetLayout(
DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS)};
VkPushConstantRange push_constant_range = {
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, PUSH_CONSTANT_BUFFER_SIZE};
VkPipelineLayoutCreateInfo pipeline_layout_info = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
nullptr,
0,
static_cast<u32>(ArraySize(sets)),
sets,
1,
&push_constant_range};
res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &pipeline_layout_info, nullptr,
&m_pipeline_layout);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreatePipelineLayout failed: ");
return false;
}
return true;
}
} // namespace Vulkan
@@ -1,50 +0,0 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include "Common/CommonTypes.h"
#include "VideoBackends/Vulkan/StreamBuffer.h"
#include "VideoCommon/TextureDecoder.h"
namespace Vulkan
{
class Texture2D;
// Since this converter uses a uniform texel buffer, we can't use the general pipeline generators.
class PaletteTextureConverter
{
public:
PaletteTextureConverter();
~PaletteTextureConverter();
bool Initialize();
void ConvertTexture(VkCommandBuffer command_buffer, VkRenderPass render_pass,
VkFramebuffer dst_framebuffer, Texture2D* src_texture, u32 width, u32 height,
void* palette, TlutFormat format, u32 src_format);
private:
static const size_t NUM_PALETTE_CONVERSION_SHADERS = 3;
bool CreateBuffers();
bool CompileShaders();
bool CreateDescriptorLayout();
VkDescriptorSetLayout m_palette_set_layout = VK_NULL_HANDLE;
VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE;
std::array<VkShaderModule, NUM_PALETTE_CONVERSION_SHADERS> m_shaders = {};
std::unique_ptr<StreamBuffer> m_palette_stream_buffer;
VkBufferView m_palette_buffer_view = VK_NULL_HANDLE;
std::unique_ptr<StreamBuffer> m_uniform_buffer;
};
} // namespace Vulkan
@@ -300,8 +300,8 @@ void RasterFont::PrintMultiLineText(VkRenderPass render_pass, const std::string&
return;
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
g_object_cache->GetPushConstantPipelineLayout(), render_pass,
m_vertex_shader, VK_NULL_HANDLE, m_fragment_shader);
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT),
render_pass, m_vertex_shader, VK_NULL_HANDLE, m_fragment_shader);
UtilityShaderVertex* vertices =
draw.ReserveVertices(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, text.length() * 6);
@@ -448,7 +448,7 @@ void Renderer::ClearScreen(const EFBRectangle& rc, bool color_enable, bool alpha
// No need to start a new render pass, but we do need to restore viewport state
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
g_object_cache->GetStandardPipelineLayout(),
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
FramebufferManager::GetInstance()->GetEFBLoadRenderPass(),
g_object_cache->GetPassthroughVertexShader(),
g_object_cache->GetPassthroughGeometryShader(), m_clear_fragment_shader);
@@ -613,6 +613,9 @@ void Renderer::DrawVirtualXFB(VkRenderPass render_pass, const TargetRectangle& t
for (u32 i = 0; i < xfb_count; ++i)
{
const XFBSource* xfb_source = static_cast<const XFBSource*>(xfb_sources[i]);
xfb_source->GetTexture()->GetTexture()->TransitionToLayout(
g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
TargetRectangle source_rect = xfb_source->sourceRc;
TargetRectangle draw_rect;
@@ -646,6 +649,9 @@ void Renderer::DrawRealXFB(VkRenderPass render_pass, const TargetRectangle& targ
for (u32 i = 0; i < xfb_count; ++i)
{
const XFBSource* xfb_source = static_cast<const XFBSource*>(xfb_sources[i]);
xfb_source->GetTexture()->GetTexture()->TransitionToLayout(
g_command_buffer_mgr->GetCurrentCommandBuffer(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
TargetRectangle source_rect = xfb_source->sourceRc;
TargetRectangle draw_rect = target_rect;
source_rect.right -= fb_stride - fb_width;
@@ -879,7 +885,7 @@ void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_r
// Set up common data
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
g_object_cache->GetStandardPipelineLayout(), render_pass,
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), render_pass,
g_object_cache->GetPassthroughVertexShader(), VK_NULL_HANDLE,
m_blit_fragment_shader);
@@ -52,6 +52,7 @@ static const char SHADER_HEADER[] = R"(
#define UBO_BINDING(packing, x) layout(packing, set = 0, binding = (x - 1))
#define SAMPLER_BINDING(x) layout(set = 1, binding = x)
#define SSBO_BINDING(x) layout(set = 2, binding = x)
#define TEXEL_BUFFER_BINDING(x) layout(set = 2, binding = x)
#define VARYING_LOCATION(x) layout(location = x)
#define FORCE_EARLY_Z layout(early_fragment_tests) in
@@ -80,6 +80,9 @@ void StagingBuffer::InvalidateGPUCache(VkCommandBuffer command_buffer,
VkPipelineStageFlagBits dest_pipeline_stage,
VkDeviceSize offset, VkDeviceSize size)
{
if (m_coherent)
return;
_assert_((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE));
Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_HOST_WRITE_BIT, dest_access_flags,
offset, size, VK_PIPELINE_STAGE_HOST_BIT, dest_pipeline_stage);
@@ -90,6 +93,9 @@ void StagingBuffer::PrepareForGPUWrite(VkCommandBuffer command_buffer,
VkPipelineStageFlagBits dst_pipeline_stage,
VkDeviceSize offset, VkDeviceSize size)
{
if (m_coherent)
return;
_assert_((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE));
Util::BufferMemoryBarrier(command_buffer, m_buffer, 0, dst_access_flags, offset, size,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, dst_pipeline_stage);
@@ -99,6 +105,9 @@ void StagingBuffer::FlushGPUCache(VkCommandBuffer command_buffer, VkAccessFlagBi
VkPipelineStageFlagBits src_pipeline_stage, VkDeviceSize offset,
VkDeviceSize size)
{
if (m_coherent)
return;
_assert_((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE));
Util::BufferMemoryBarrier(command_buffer, m_buffer, src_access_flags, VK_ACCESS_HOST_READ_BIT,
offset, size, src_pipeline_stage, VK_PIPELINE_STAGE_HOST_BIT);
@@ -136,8 +145,9 @@ void StagingBuffer::Write(VkDeviceSize offset, const void* data, size_t size,
FlushCPUCache(offset, size);
}
std::unique_ptr<Vulkan::StagingBuffer>
StagingBuffer::Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsageFlags usage)
bool StagingBuffer::AllocateBuffer(STAGING_BUFFER_TYPE type, VkDeviceSize size,
VkBufferUsageFlags usage, VkBuffer* out_buffer,
VkDeviceMemory* out_memory, bool* out_coherent)
{
VkBufferCreateInfo buffer_create_info = {
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType
@@ -149,24 +159,22 @@ StagingBuffer::Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsage
0, // uint32_t queueFamilyIndexCount
nullptr // const uint32_t* pQueueFamilyIndices
};
VkBuffer buffer;
VkResult res =
vkCreateBuffer(g_vulkan_context->GetDevice(), &buffer_create_info, nullptr, &buffer);
vkCreateBuffer(g_vulkan_context->GetDevice(), &buffer_create_info, nullptr, out_buffer);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: ");
return nullptr;
return false;
}
VkMemoryRequirements requirements;
vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), buffer, &requirements);
vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), *out_buffer, &requirements);
bool is_coherent;
u32 type_index;
if (type == STAGING_BUFFER_TYPE_UPLOAD)
type_index = g_vulkan_context->GetUploadMemoryType(requirements.memoryTypeBits, &is_coherent);
type_index = g_vulkan_context->GetUploadMemoryType(requirements.memoryTypeBits, out_coherent);
else
type_index = g_vulkan_context->GetReadbackMemoryType(requirements.memoryTypeBits, &is_coherent);
type_index = g_vulkan_context->GetReadbackMemoryType(requirements.memoryTypeBits, out_coherent);
VkMemoryAllocateInfo memory_allocate_info = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType
@@ -174,25 +182,36 @@ StagingBuffer::Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsage
requirements.size, // VkDeviceSize allocationSize
type_index // uint32_t memoryTypeIndex
};
VkDeviceMemory memory;
res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory);
res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, out_memory);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: ");
vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr);
return nullptr;
vkDestroyBuffer(g_vulkan_context->GetDevice(), *out_buffer, nullptr);
return false;
}
res = vkBindBufferMemory(g_vulkan_context->GetDevice(), buffer, memory, 0);
res = vkBindBufferMemory(g_vulkan_context->GetDevice(), *out_buffer, *out_memory, 0);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkBindBufferMemory failed: ");
vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr);
vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr);
return nullptr;
vkDestroyBuffer(g_vulkan_context->GetDevice(), *out_buffer, nullptr);
vkFreeMemory(g_vulkan_context->GetDevice(), *out_memory, nullptr);
return false;
}
return std::make_unique<Vulkan::StagingBuffer>(type, buffer, memory, size, is_coherent);
return true;
}
std::unique_ptr<StagingBuffer> StagingBuffer::Create(STAGING_BUFFER_TYPE type, VkDeviceSize size,
VkBufferUsageFlags usage)
{
VkBuffer buffer;
VkDeviceMemory memory;
bool coherent;
if (!AllocateBuffer(type, size, usage, &buffer, &memory, &coherent))
return nullptr;
return std::make_unique<StagingBuffer>(type, buffer, memory, size, coherent);
}
} // namespace Vulkan
@@ -16,7 +16,7 @@ class StagingBuffer
public:
StagingBuffer(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize size,
bool coherent);
~StagingBuffer();
virtual ~StagingBuffer();
STAGING_BUFFER_TYPE GetType() const { return m_type; }
VkDeviceSize GetSize() const { return m_size; }
@@ -33,6 +33,7 @@ public:
void FlushCPUCache(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE);
// Upload part 2: Prepare for device read from the GPU side
// Implicit when submitting the command buffer, so rarely needed.
void InvalidateGPUCache(VkCommandBuffer command_buffer, VkAccessFlagBits dst_access_flags,
VkPipelineStageFlagBits dst_pipeline_stage, VkDeviceSize offset = 0,
VkDeviceSize size = VK_WHOLE_SIZE);
@@ -59,6 +60,10 @@ public:
VkBufferUsageFlags usage);
protected:
// Allocates the resources needed to create a staging buffer.
static bool AllocateBuffer(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsageFlags usage,
VkBuffer* out_buffer, VkDeviceMemory* out_memory, bool* out_coherent);
STAGING_BUFFER_TYPE m_type;
VkBuffer m_buffer;
VkDeviceMemory m_memory;
@@ -14,16 +14,16 @@
namespace Vulkan
{
StagingTexture2D::StagingTexture2D(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format,
u32 stride)
: m_type(type), m_width(width), m_height(height), m_format(format),
m_texel_size(Util::GetTexelSize(format)), m_row_stride(stride)
StagingTexture2D::StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory,
VkDeviceSize size, bool coherent, u32 width, u32 height,
VkFormat format, u32 stride)
: StagingBuffer(type, buffer, memory, size, coherent), m_width(width), m_height(height),
m_format(format), m_texel_size(Util::GetTexelSize(format)), m_row_stride(stride)
{
}
StagingTexture2D::~StagingTexture2D()
{
_assert_(!m_map_pointer);
}
void StagingTexture2D::ReadTexel(u32 x, u32 y, void* data, size_t data_size) const
@@ -96,283 +96,13 @@ void StagingTexture2D::WriteTexels(u32 x, u32 y, u32 width, u32 height, const vo
}
}
std::unique_ptr<StagingTexture2D> StagingTexture2D::Create(STAGING_BUFFER_TYPE type, u32 width,
u32 height, VkFormat format)
{
// TODO: Using a buffer here as opposed to a linear texture is faster on AMD.
// NVIDIA also seems faster with buffers over textures.
#if 0
// Check for support for this format as a linear texture.
// Some drivers don't support this (e.g. adreno).
VkImageFormatProperties properties;
VkResult res = vkGetPhysicalDeviceImageFormatProperties(
g_object_cache->GetPhysicalDevice(), format, VK_IMAGE_TYPE_2D, VK_IMAGE_TILING_LINEAR,
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, &properties);
if (res == VK_SUCCESS && width <= properties.maxExtent.width &&
height <= properties.maxExtent.height)
{
return StagingTexture2DLinear::Create(type, width, height, format);
}
#endif
// Fall back to a buffer copy.
return StagingTexture2DBuffer::Create(type, width, height, format);
}
StagingTexture2DLinear::StagingTexture2DLinear(STAGING_BUFFER_TYPE type, u32 width, u32 height,
VkFormat format, u32 stride, VkImage image,
VkDeviceMemory memory, VkDeviceSize size,
bool coherent)
: StagingTexture2D(type, width, height, format, stride), m_image(image), m_memory(memory),
m_size(size), m_layout(VK_IMAGE_LAYOUT_PREINITIALIZED), m_coherent(coherent)
{
}
StagingTexture2DLinear::~StagingTexture2DLinear()
{
if (m_map_pointer)
Unmap();
g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_memory);
g_command_buffer_mgr->DeferImageDestruction(m_image);
}
void StagingTexture2DLinear::CopyFromImage(VkCommandBuffer command_buffer, VkImage image,
VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width,
u32 height, u32 level, u32 layer)
{
// Prepare the buffer for copying.
// We don't care about the existing contents, so set to UNDEFINED.
VkImageMemoryBarrier before_transfer_barrier = {
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType
nullptr, // const void* pNext
0, // VkAccessFlags srcAccessMask
VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout
VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex
VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex
m_image, // VkImage image
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // VkImageSubresourceRange subresourceRange
};
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1,
&before_transfer_barrier);
// Issue the image copy, gpu -> host.
VkImageCopy copy_region = {
{src_aspect, level, layer, 1}, // VkImageSubresourceLayers srcSubresource
{static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D srcOffset
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, // VkImageSubresourceLayers dstSubresource
{0, 0, 0}, // VkOffset3D dstOffset
{width, height, 1} // VkExtent3D extent
};
vkCmdCopyImage(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_region);
// Ensure writes are visible to the host.
VkImageMemoryBarrier visible_barrier = {
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType
nullptr, // const void* pNext
VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags srcAccessMask
VK_ACCESS_HOST_READ_BIT, // VkAccessFlags dstAccessMask
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout oldLayout
VK_IMAGE_LAYOUT_GENERAL, // VkImageLayout newLayout
VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex
VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex
m_image, // VkImage image
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // VkImageSubresourceRange subresourceRange
};
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT,
0, 0, nullptr, 0, nullptr, 1, &visible_barrier);
m_layout = VK_IMAGE_LAYOUT_GENERAL;
// Invalidate memory range if currently mapped.
if (m_map_pointer && !m_coherent)
{
VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory,
m_map_offset, m_map_size};
vkInvalidateMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range);
}
}
void StagingTexture2DLinear::CopyToImage(VkCommandBuffer command_buffer, VkImage image,
VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width,
u32 height, u32 level, u32 layer)
{
// Flush memory range if currently mapped.
if (m_map_pointer && !m_coherent)
{
VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory,
m_map_offset, m_map_size};
vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range);
}
// Ensure any writes to the image are visible to the GPU.
VkImageMemoryBarrier barrier = {
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType
nullptr, // const void* pNext
VK_ACCESS_HOST_WRITE_BIT, // VkAccessFlags srcAccessMask
VK_ACCESS_TRANSFER_READ_BIT, // VkAccessFlags dstAccessMask
m_layout, // VkImageLayout oldLayout
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout newLayout
VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex
VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex
m_image, // VkImage image
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1} // VkImageSubresourceRange subresourceRange
};
vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 0, nullptr, 1, &barrier);
m_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
// Issue the image copy, host -> gpu.
VkImageCopy copy_region = {
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, // VkImageSubresourceLayers srcSubresource
{0, 0, 0}, // VkOffset3D srcOffset
{dst_aspect, level, layer, 1}, // VkImageSubresourceLayers dstSubresource
{static_cast<s32>(x), static_cast<s32>(y), 0}, // VkOffset3D dstOffset
{width, height, 1} // VkExtent3D extent
};
vkCmdCopyImage(command_buffer, m_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_region);
}
bool StagingTexture2DLinear::Map(VkDeviceSize offset /* = 0 */,
VkDeviceSize size /* = VK_WHOLE_SIZE */)
{
m_map_offset = offset;
if (size == VK_WHOLE_SIZE)
m_map_size = m_size - offset;
else
m_map_size = size;
_assert_(!m_map_pointer);
_assert_(m_map_offset + m_map_size <= m_size);
void* map_pointer;
VkResult res = vkMapMemory(g_vulkan_context->GetDevice(), m_memory, m_map_offset, m_map_size, 0,
&map_pointer);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkMapMemory failed: ");
return false;
}
m_map_pointer = reinterpret_cast<char*>(map_pointer);
return true;
}
void StagingTexture2DLinear::Unmap()
{
_assert_(m_map_pointer);
vkUnmapMemory(g_vulkan_context->GetDevice(), m_memory);
m_map_pointer = nullptr;
m_map_offset = 0;
m_map_size = 0;
}
std::unique_ptr<StagingTexture2D>
StagingTexture2DLinear::Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format)
{
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
VkImageCreateInfo create_info = {
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType
nullptr, // const void* pNext
0, // VkImageCreateFlags flags
VK_IMAGE_TYPE_2D, // VkImageType imageType
format, // VkFormat format
{width, height, 1}, // VkExtent3D extent
1, // uint32_t mipLevels
1, // uint32_t arrayLayers
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
VK_IMAGE_TILING_LINEAR, // VkImageTiling tiling
usage, // VkImageUsageFlags usage
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode
0, // uint32_t queueFamilyIndexCount
nullptr, // const uint32_t* pQueueFamilyIndices
VK_IMAGE_LAYOUT_PREINITIALIZED // VkImageLayout initialLayout
};
VkImage image;
VkResult res = vkCreateImage(g_vulkan_context->GetDevice(), &create_info, nullptr, &image);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateImage failed: ");
return nullptr;
}
VkMemoryRequirements memory_requirements;
vkGetImageMemoryRequirements(g_vulkan_context->GetDevice(), image, &memory_requirements);
bool is_coherent;
u32 memory_type_index;
if (type == STAGING_BUFFER_TYPE_READBACK)
{
memory_type_index =
g_vulkan_context->GetReadbackMemoryType(memory_requirements.memoryTypeBits, &is_coherent);
}
else
{
memory_type_index =
g_vulkan_context->GetUploadMemoryType(memory_requirements.memoryTypeBits, &is_coherent);
}
VkMemoryAllocateInfo memory_allocate_info = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType
nullptr, // const void* pNext
memory_requirements.size, // VkDeviceSize allocationSize
memory_type_index // uint32_t memoryTypeIndex
};
VkDeviceMemory memory;
res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: ");
vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr);
return nullptr;
}
res = vkBindImageMemory(g_vulkan_context->GetDevice(), image, memory, 0);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkBindImageMemory failed: ");
vkDestroyImage(g_vulkan_context->GetDevice(), image, nullptr);
vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr);
return nullptr;
}
// Assume tight packing. Is this correct?
u32 stride = width * Util::GetTexelSize(format);
return std::make_unique<StagingTexture2DLinear>(type, width, height, format, stride, image,
memory, memory_requirements.size, is_coherent);
}
StagingTexture2DBuffer::StagingTexture2DBuffer(STAGING_BUFFER_TYPE type, u32 width, u32 height,
VkFormat format, u32 stride, VkBuffer buffer,
VkDeviceMemory memory, VkDeviceSize size,
bool coherent)
: StagingTexture2D(type, width, height, format, stride), m_buffer(buffer), m_memory(memory),
m_size(size), m_coherent(coherent)
{
}
StagingTexture2DBuffer::~StagingTexture2DBuffer()
{
if (m_map_pointer)
Unmap();
g_command_buffer_mgr->DeferDeviceMemoryDestruction(m_memory);
g_command_buffer_mgr->DeferBufferDestruction(m_buffer);
}
void StagingTexture2DBuffer::CopyFromImage(VkCommandBuffer command_buffer, VkImage image,
VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width,
u32 height, u32 level, u32 layer)
void StagingTexture2D::CopyFromImage(VkCommandBuffer command_buffer, VkImage image,
VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width,
u32 height, u32 level, u32 layer)
{
// Issue the image->buffer copy.
VkBufferImageCopy image_copy = {
0, // VkDeviceSize bufferOffset
y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset
m_width, // uint32_t bufferRowLength
0, // uint32_t bufferImageHeight
{src_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource
@@ -382,42 +112,28 @@ void StagingTexture2DBuffer::CopyFromImage(VkCommandBuffer command_buffer, VkIma
vkCmdCopyImageToBuffer(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1,
&image_copy);
// Ensure the write has completed.
VkDeviceSize copy_size = m_row_stride * height;
Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_HOST_READ_BIT, 0, copy_size, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_HOST_BIT);
// If we're still mapped, invalidate the mapped range
if (m_map_pointer && !m_coherent)
{
VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory,
m_map_offset, m_map_size};
vkInvalidateMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range);
}
// Flush CPU and GPU caches if not coherent mapping.
VkDeviceSize buffer_flush_offset = y * m_row_stride;
VkDeviceSize buffer_flush_size = height * m_row_stride;
FlushGPUCache(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
buffer_flush_offset, buffer_flush_size);
InvalidateCPUCache(buffer_flush_offset, buffer_flush_size);
}
void StagingTexture2DBuffer::CopyToImage(VkCommandBuffer command_buffer, VkImage image,
VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width,
u32 height, u32 level, u32 layer)
void StagingTexture2D::CopyToImage(VkCommandBuffer command_buffer, VkImage image,
VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width,
u32 height, u32 level, u32 layer)
{
// If we're still mapped, flush the mapped range
if (m_map_pointer && !m_coherent)
{
VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory,
m_map_offset, m_map_size};
vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range);
}
// Flush CPU and GPU caches if not coherent mapping.
VkDeviceSize buffer_flush_offset = y * m_row_stride;
VkDeviceSize buffer_flush_size = height * m_row_stride;
FlushCPUCache(buffer_flush_offset, buffer_flush_size);
InvalidateGPUCache(command_buffer, VK_ACCESS_HOST_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
buffer_flush_offset, buffer_flush_size);
// Ensure writes are visible to GPU.
VkDeviceSize copy_size = m_row_stride * height;
Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_HOST_WRITE_BIT,
VK_ACCESS_TRANSFER_READ_BIT, 0, copy_size, VK_PIPELINE_STAGE_HOST_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT);
// Issue the buffer->image copy
// Issue the buffer->image copy.
VkBufferImageCopy image_copy = {
0, // VkDeviceSize bufferOffset
y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset
m_width, // uint32_t bufferRowLength
0, // uint32_t bufferImageHeight
{dst_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource
@@ -428,109 +144,21 @@ void StagingTexture2DBuffer::CopyToImage(VkCommandBuffer command_buffer, VkImage
&image_copy);
}
bool StagingTexture2DBuffer::Map(VkDeviceSize offset /* = 0 */,
VkDeviceSize size /* = VK_WHOLE_SIZE */)
{
m_map_offset = offset;
if (size == VK_WHOLE_SIZE)
m_map_size = m_size - offset;
else
m_map_size = size;
_assert_(!m_map_pointer);
_assert_(m_map_offset + m_map_size <= m_size);
void* map_pointer;
VkResult res = vkMapMemory(g_vulkan_context->GetDevice(), m_memory, m_map_offset, m_map_size, 0,
&map_pointer);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkMapMemory failed: ");
return false;
}
m_map_pointer = reinterpret_cast<char*>(map_pointer);
return true;
}
void StagingTexture2DBuffer::Unmap()
{
_assert_(m_map_pointer);
vkUnmapMemory(g_vulkan_context->GetDevice(), m_memory);
m_map_pointer = nullptr;
m_map_offset = 0;
m_map_size = 0;
}
std::unique_ptr<StagingTexture2D>
StagingTexture2DBuffer::Create(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format)
std::unique_ptr<StagingTexture2D> StagingTexture2D::Create(STAGING_BUFFER_TYPE type, u32 width,
u32 height, VkFormat format)
{
// Assume tight packing.
u32 row_stride = Util::GetTexelSize(format) * width;
u32 buffer_size = row_stride * height;
u32 stride = Util::GetTexelSize(format) * width;
u32 size = stride * height;
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
VkBufferCreateInfo buffer_create_info = {
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType
nullptr, // const void* pNext
0, // VkBufferCreateFlags flags
buffer_size, // VkDeviceSize size
usage, // VkBufferUsageFlags usage
VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode
0, // uint32_t queueFamilyIndexCount
nullptr // const uint32_t* pQueueFamilyIndices
};
VkBuffer buffer;
VkResult res =
vkCreateBuffer(g_vulkan_context->GetDevice(), &buffer_create_info, nullptr, &buffer);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: ");
return nullptr;
}
VkMemoryRequirements memory_requirements;
vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), buffer, &memory_requirements);
bool is_coherent;
u32 memory_type_index;
if (type == STAGING_BUFFER_TYPE_READBACK)
{
memory_type_index =
g_vulkan_context->GetReadbackMemoryType(memory_requirements.memoryTypeBits, &is_coherent);
}
else
{
memory_type_index =
g_vulkan_context->GetUploadMemoryType(memory_requirements.memoryTypeBits, &is_coherent);
}
VkMemoryAllocateInfo memory_allocate_info = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType
nullptr, // const void* pNext
memory_requirements.size, // VkDeviceSize allocationSize
memory_type_index // uint32_t memoryTypeIndex
};
VkDeviceMemory memory;
res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: ");
vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr);
bool coherent;
if (!AllocateBuffer(type, size, usage, &buffer, &memory, &coherent))
return nullptr;
}
res = vkBindBufferMemory(g_vulkan_context->GetDevice(), buffer, memory, 0);
if (res != VK_SUCCESS)
{
LOG_VULKAN_ERROR(res, "vkBindBufferMemory failed: ");
vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr);
vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr);
return nullptr;
}
return std::make_unique<StagingTexture2DBuffer>(type, width, height, format, row_stride, buffer,
memory, buffer_size, is_coherent);
return std::make_unique<StagingTexture2D>(type, buffer, memory, size, coherent, width, height,
format, stride);
}
} // namespace Vulkan
@@ -9,29 +9,26 @@
#include "Common/CommonTypes.h"
#include "VideoBackends/Vulkan/Constants.h"
#include "VideoBackends/Vulkan/StagingBuffer.h"
namespace Vulkan
{
class StagingTexture2D
class StagingTexture2D final : public StagingBuffer
{
public:
StagingTexture2D(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format, u32 stride);
virtual ~StagingTexture2D();
StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory,
VkDeviceSize size, bool coherent, u32 width, u32 height, VkFormat format,
u32 stride);
~StagingTexture2D();
STAGING_BUFFER_TYPE GetType() const { return m_type; }
u32 GetWidth() const { return m_width; }
u32 GetHeight() const { return m_height; }
VkFormat GetFormat() const { return m_format; }
u32 GetRowStride() const { return m_row_stride; }
u32 GetTexelSize() const { return m_texel_size; }
bool IsMapped() const { return m_map_pointer != nullptr; }
const char* GetMapPointer() const { return m_map_pointer; }
char* GetMapPointer() { return m_map_pointer; }
VkDeviceSize GetMapOffset() const { return m_map_offset; }
VkDeviceSize GetMapSize() const { return m_map_size; }
// Requires Map() to be called first.
const char* GetRowPointer(u32 row) const { return m_map_pointer + row * m_row_stride; }
char* GetRowPointer(u32 row) { return m_map_pointer + row * m_row_stride; }
// Requires Map() to be called first.
void ReadTexel(u32 x, u32 y, void* data, size_t data_size) const;
void WriteTexel(u32 x, u32 y, const void* data, size_t data_size);
void ReadTexels(u32 x, u32 y, u32 width, u32 height, void* data, u32 data_stride) const;
@@ -39,89 +36,23 @@ public:
// Assumes that image is in TRANSFER_SRC layout.
// Results are not ready until command_buffer has been executed.
virtual void CopyFromImage(VkCommandBuffer command_buffer, VkImage image,
VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width, u32 height,
u32 level, u32 layer) = 0;
void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect,
u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer);
// Assumes that image is in TRANSFER_DST layout.
// Buffer is not safe for re-use until after command_buffer has been executed.
virtual void CopyToImage(VkCommandBuffer command_buffer, VkImage image,
VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, u32 height,
u32 level, u32 layer) = 0;
virtual bool Map(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) = 0;
virtual void Unmap() = 0;
void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect,
u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer);
// Creates the optimal format of image copy.
static std::unique_ptr<StagingTexture2D> Create(STAGING_BUFFER_TYPE type, u32 width, u32 height,
VkFormat format);
protected:
STAGING_BUFFER_TYPE m_type;
u32 m_width;
u32 m_height;
VkFormat m_format;
u32 m_texel_size;
u32 m_row_stride;
char* m_map_pointer = nullptr;
VkDeviceSize m_map_offset = 0;
VkDeviceSize m_map_size = 0;
};
class StagingTexture2DLinear : public StagingTexture2D
{
public:
StagingTexture2DLinear(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format,
u32 stride, VkImage image, VkDeviceMemory memory, VkDeviceSize size,
bool coherent);
~StagingTexture2DLinear();
void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect,
u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) override;
void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect,
u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) override;
bool Map(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) override;
void Unmap() override;
static std::unique_ptr<StagingTexture2D> Create(STAGING_BUFFER_TYPE type, u32 width, u32 height,
VkFormat format);
private:
VkImage m_image;
VkDeviceMemory m_memory;
VkDeviceSize m_size;
VkImageLayout m_layout;
bool m_coherent;
};
class StagingTexture2DBuffer : public StagingTexture2D
{
public:
StagingTexture2DBuffer(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format,
u32 stride, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize size,
bool coherent);
~StagingTexture2DBuffer();
void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect,
u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) override;
void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect,
u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer) override;
bool Map(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) override;
void Unmap() override;
static std::unique_ptr<StagingTexture2D> Create(STAGING_BUFFER_TYPE type, u32 width, u32 height,
VkFormat format);
private:
VkBuffer m_buffer;
VkDeviceMemory m_memory;
VkDeviceSize m_size;
bool m_coherent;
};
}
@@ -75,8 +75,8 @@ bool StateTracker::Initialize()
m_pipeline_state.rasterization_state.depth_clamp = VK_TRUE;
// BBox is disabled by default.
m_pipeline_state.pipeline_layout = g_object_cache->GetStandardPipelineLayout();
m_num_active_descriptor_sets = NUM_DESCRIPTOR_SETS - 1;
m_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS;
m_bbox_enabled = false;
// Initialize all samplers to point by default
@@ -164,8 +164,8 @@ bool StateTracker::PrecachePipelineUID(const SerializedPipelineUID& uid)
// vertex loader that uses this format, since we need it to create a pipeline.
pinfo.vertex_format = VertexFormat::GetOrCreateMatchingFormat(uid.vertex_decl);
pinfo.pipeline_layout = uid.ps_uid.GetUidData()->bounding_box ?
g_object_cache->GetBBoxPipelineLayout() :
g_object_cache->GetStandardPipelineLayout();
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX) :
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
pinfo.vs = g_object_cache->GetVertexShaderForUid(uid.vs_uid);
if (pinfo.vs == VK_NULL_HANDLE)
{
@@ -544,17 +544,17 @@ void StateTracker::SetBBoxEnable(bool enable)
// Change the number of active descriptor sets, as well as the pipeline layout
if (enable)
{
m_pipeline_state.pipeline_layout = g_object_cache->GetBBoxPipelineLayout();
m_num_active_descriptor_sets = NUM_DESCRIPTOR_SETS;
m_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX);
m_num_active_descriptor_sets = NUM_GX_DRAW_WITH_BBOX_DESCRIPTOR_SETS;
// The bbox buffer never changes, so we defer descriptor updates until it is enabled.
if (m_descriptor_sets[DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS] == VK_NULL_HANDLE)
if (m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] == VK_NULL_HANDLE)
m_dirty_flags |= DIRTY_FLAG_PS_SSBO;
}
else
{
m_pipeline_state.pipeline_layout = g_object_cache->GetStandardPipelineLayout();
m_num_active_descriptor_sets = NUM_DESCRIPTOR_SETS - 1;
m_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS;
}
m_dirty_flags |= DIRTY_FLAG_PIPELINE | DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
@@ -731,7 +731,8 @@ bool StateTracker::Bind(bool rebind_all /*= false*/)
{
vkCmdBindDescriptorSets(
command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_state.pipeline_layout,
DESCRIPTOR_SET_UNIFORM_BUFFERS, 1, &m_descriptor_sets[DESCRIPTOR_SET_UNIFORM_BUFFERS],
DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS, 1,
&m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS],
NUM_UBO_DESCRIPTOR_SET_BINDINGS, m_bindings.uniform_buffer_offsets.data());
}
@@ -791,7 +792,12 @@ void StateTracker::OnEndFrame()
u32 interval = static_cast<u32>(g_ActiveConfig.iCommandBufferExecuteInterval);
for (u32 draw_counter : m_cpu_accesses_this_frame)
{
// We don't want to waste executing command buffers for only a few draws, so set a minimum.
// Leave last_draw_counter as-is, so we get the correct number of draws between submissions.
u32 draw_count = draw_counter - last_draw_counter;
if (draw_count < MINIMUM_DRAW_CALLS_PER_COMMAND_BUFFER_FOR_READBACK)
continue;
if (draw_count <= interval)
{
u32 mid_point = draw_count / 2;
@@ -806,6 +812,8 @@ void StateTracker::OnEndFrame()
counter += interval;
}
}
last_draw_counter = draw_counter;
}
}
@@ -921,10 +929,10 @@ bool StateTracker::UpdateDescriptorSet()
u32 num_writes = 0;
if (m_dirty_flags & (DIRTY_FLAG_VS_UBO | DIRTY_FLAG_GS_UBO | DIRTY_FLAG_PS_UBO) ||
m_descriptor_sets[DESCRIPTOR_SET_UNIFORM_BUFFERS] == VK_NULL_HANDLE)
m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS] == VK_NULL_HANDLE)
{
VkDescriptorSetLayout layout =
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_UNIFORM_BUFFERS);
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS);
VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(layout);
if (set == VK_NULL_HANDLE)
return false;
@@ -943,15 +951,15 @@ bool StateTracker::UpdateDescriptorSet()
nullptr};
}
m_descriptor_sets[DESCRIPTOR_SET_UNIFORM_BUFFERS] = set;
m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_UNIFORM_BUFFERS] = set;
m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
}
if (m_dirty_flags & DIRTY_FLAG_PS_SAMPLERS ||
m_descriptor_sets[DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS] == VK_NULL_HANDLE)
m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS] == VK_NULL_HANDLE)
{
VkDescriptorSetLayout layout =
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS);
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS);
VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(layout);
if (set == VK_NULL_HANDLE)
return false;
@@ -974,16 +982,16 @@ bool StateTracker::UpdateDescriptorSet()
}
}
m_descriptor_sets[DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS] = set;
m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS] = set;
m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
}
if (m_bbox_enabled &&
(m_dirty_flags & DIRTY_FLAG_PS_SSBO ||
m_descriptor_sets[DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS] == VK_NULL_HANDLE))
m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] == VK_NULL_HANDLE))
{
VkDescriptorSetLayout layout =
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS);
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS);
VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(layout);
if (set == VK_NULL_HANDLE)
return false;
@@ -999,7 +1007,7 @@ bool StateTracker::UpdateDescriptorSet()
&m_bindings.ps_ssbo,
nullptr};
m_descriptor_sets[DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS] = set;
m_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] = set;
m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
}
@@ -132,6 +132,13 @@ private:
VkPrimitiveTopology primitive_topology;
};
// Number of descriptor sets for game draws.
enum
{
NUM_GX_DRAW_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS + 1,
NUM_GX_DRAW_WITH_BBOX_DESCRIPTOR_SETS = DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER + 1
};
enum DITRY_FLAG : u32
{
DIRTY_FLAG_VS_UBO = (1 << 0),
@@ -202,7 +209,7 @@ private:
VkPipeline m_pipeline_object = VK_NULL_HANDLE;
// shader bindings
std::array<VkDescriptorSet, NUM_DESCRIPTOR_SETS> m_descriptor_sets = {};
std::array<VkDescriptorSet, NUM_DESCRIPTOR_SET_BIND_POINTS> m_descriptor_sets = {};
struct
{
std::array<VkDescriptorBufferInfo, NUM_UBO_DESCRIPTOR_SET_BINDINGS> uniform_buffer_bindings =
File diff suppressed because it is too large Load Diff
@@ -12,10 +12,9 @@
namespace Vulkan
{
class PaletteTextureConverter;
class TextureConverter;
class StateTracker;
class Texture2D;
class TextureEncoder;
class TextureCache : public TextureCacheBase
{
@@ -49,6 +48,7 @@ public:
static TextureCache* GetInstance();
TextureConverter* GetTextureConverter() const { return m_texture_converter.get(); }
bool Initialize();
bool CompileShaders() override;
@@ -66,17 +66,8 @@ public:
void CopyRectangleFromTexture(TCacheEntry* dst_texture, const MathUtil::Rectangle<int>& dst_rect,
Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect);
// Encodes texture to guest memory in XFB (YUYV) format.
void EncodeYUYVTextureToMemory(void* dst_ptr, u32 dst_width, u32 dst_stride, u32 dst_height,
Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect);
// Decodes data from guest memory in XFB (YUYV) format to a RGBA format texture on the GPU.
void DecodeYUYVTextureFromMemory(TCacheEntry* dst_texture, const void* src_ptr, u32 src_width,
u32 src_stride, u32 src_height);
private:
bool CreateRenderPasses();
VkRenderPass GetRenderPassForTextureUpdate(const Texture2D* texture) const;
// Copies the contents of a texture using vkCmdCopyImage
void CopyTextureRectangle(TCacheEntry* dst_texture, const MathUtil::Rectangle<int>& dst_rect,
@@ -86,20 +77,15 @@ private:
void ScaleTextureRectangle(TCacheEntry* dst_texture, const MathUtil::Rectangle<int>& dst_rect,
Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect);
VkRenderPass m_initialize_render_pass = VK_NULL_HANDLE;
VkRenderPass m_update_render_pass = VK_NULL_HANDLE;
VkRenderPass m_render_pass = VK_NULL_HANDLE;
std::unique_ptr<StreamBuffer> m_texture_upload_buffer;
std::unique_ptr<TextureEncoder> m_texture_encoder;
std::unique_ptr<PaletteTextureConverter> m_palette_texture_converter;
std::unique_ptr<TextureConverter> m_texture_converter;
VkShaderModule m_copy_shader = VK_NULL_HANDLE;
VkShaderModule m_efb_color_to_tex_shader = VK_NULL_HANDLE;
VkShaderModule m_efb_depth_to_tex_shader = VK_NULL_HANDLE;
VkShaderModule m_rgb_to_yuyv_shader = VK_NULL_HANDLE;
VkShaderModule m_yuyv_to_rgb_shader = VK_NULL_HANDLE;
};
} // namespace Vulkan
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,100 @@
// Copyright 2016 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include "Common/CommonTypes.h"
#include "VideoBackends/Vulkan/StreamBuffer.h"
#include "VideoBackends/Vulkan/TextureCache.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/TextureDecoder.h"
#include "VideoCommon/VideoCommon.h"
namespace Vulkan
{
class StagingTexture2D;
class Texture2D;
class TextureConverter
{
public:
TextureConverter();
~TextureConverter();
bool Initialize();
// Applies palette to dst_entry, using indices from src_entry.
void ConvertTexture(TextureCache::TCacheEntry* dst_entry, TextureCache::TCacheEntry* src_entry,
VkRenderPass render_pass, const void* palette, TlutFormat palette_format);
// Uses an encoding shader to copy src_texture to dest_ptr.
// NOTE: Executes the current command buffer.
void EncodeTextureToMemory(VkImageView src_texture, u8* dest_ptr, u32 format, u32 native_width,
u32 bytes_per_row, u32 num_blocks_y, u32 memory_stride,
PEControl::PixelFormat src_format, bool is_intensity,
int scale_by_half, const EFBRectangle& source);
// Encodes texture to guest memory in XFB (YUYV) format.
void EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u32 dst_stride, u32 dst_height,
Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect);
// Decodes data from guest memory in XFB (YUYV) format to a RGBA format texture on the GPU.
void DecodeYUYVTextureFromMemory(TextureCache::TCacheEntry* dst_texture, const void* src_ptr,
u32 src_width, u32 src_stride, u32 src_height);
private:
static const u32 NUM_TEXTURE_ENCODING_SHADERS = 64;
static const u32 ENCODING_TEXTURE_WIDTH = EFB_WIDTH * 4;
static const u32 ENCODING_TEXTURE_HEIGHT = 1024;
static const VkFormat ENCODING_TEXTURE_FORMAT = VK_FORMAT_B8G8R8A8_UNORM;
static const size_t NUM_PALETTE_CONVERSION_SHADERS = 3;
bool CreateTexelBuffer();
VkBufferView CreateTexelBufferView(VkFormat format) const;
bool CompilePaletteConversionShaders();
bool CompileEncodingShaders();
bool CreateEncodingRenderPass();
bool CreateEncodingTexture();
bool CreateEncodingDownloadTexture();
bool CompileYUYVConversionShaders();
// Allocates storage in the texel command buffer of the specified size.
// If the buffer does not have enough space, executes the current command buffer and tries again.
// If this is done, g_command_buffer_mgr->GetCurrentCommandBuffer() will return a different value,
// so it always should be re-obtained after calling this method.
// Once the data copy is done, call m_texel_buffer->CommitMemory(size).
bool ReserveTexelBufferStorage(size_t size, size_t alignment);
// Returns the command buffer that the texture conversion should occur in for the given texture.
// This can be the initialization/copy command buffer, or the drawing command buffer.
VkCommandBuffer GetCommandBufferForTextureConversion(const TextureCache::TCacheEntry* src_entry);
// Shared between conversion types
std::unique_ptr<StreamBuffer> m_texel_buffer;
VkBufferView m_texel_buffer_view_r16_uint = VK_NULL_HANDLE;
VkBufferView m_texel_buffer_view_rgba8_unorm = VK_NULL_HANDLE;
size_t m_texel_buffer_size = 0;
// Palette conversion - taking an indexed texture and applying palette
std::array<VkShaderModule, NUM_PALETTE_CONVERSION_SHADERS> m_palette_conversion_shaders = {};
// Texture encoding - RGBA8->GX format in memory
std::array<VkShaderModule, NUM_TEXTURE_ENCODING_SHADERS> m_encoding_shaders = {};
VkRenderPass m_encoding_render_pass = VK_NULL_HANDLE;
std::unique_ptr<Texture2D> m_encoding_render_texture;
VkFramebuffer m_encoding_render_framebuffer = VK_NULL_HANDLE;
std::unique_ptr<StagingTexture2D> m_encoding_download_texture;
// XFB encoding/decoding shaders
VkShaderModule m_rgb_to_yuyv_shader = VK_NULL_HANDLE;
VkShaderModule m_yuyv_to_rgb_shader = VK_NULL_HANDLE;
};
} // namespace Vulkan

Some files were not shown because too many files have changed in this diff Show More