Merge pull request #4462 from stenzek/vulkan-faster-xfb
Vulkan: Cleanup/refactoring, faster RealXFB
This commit is contained in:
commit
15e2133ce7
|
@ -3,7 +3,6 @@ set(SRCS
|
||||||
CommandBufferManager.cpp
|
CommandBufferManager.cpp
|
||||||
FramebufferManager.cpp
|
FramebufferManager.cpp
|
||||||
ObjectCache.cpp
|
ObjectCache.cpp
|
||||||
PaletteTextureConverter.cpp
|
|
||||||
PerfQuery.cpp
|
PerfQuery.cpp
|
||||||
RasterFont.cpp
|
RasterFont.cpp
|
||||||
Renderer.cpp
|
Renderer.cpp
|
||||||
|
@ -15,11 +14,11 @@ set(SRCS
|
||||||
SwapChain.cpp
|
SwapChain.cpp
|
||||||
Texture2D.cpp
|
Texture2D.cpp
|
||||||
TextureCache.cpp
|
TextureCache.cpp
|
||||||
TextureEncoder.cpp
|
TextureConverter.cpp
|
||||||
Util.cpp
|
Util.cpp
|
||||||
VertexFormat.cpp
|
VertexFormat.cpp
|
||||||
VertexManager.cpp
|
VertexManager.cpp
|
||||||
VulkanContext.cpp
|
VulkanContext.cpp
|
||||||
VulkanLoader.cpp
|
VulkanLoader.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
)
|
)
|
||||||
|
|
|
@ -23,13 +23,46 @@ enum STAGING_BUFFER_TYPE
|
||||||
STAGING_BUFFER_TYPE_READBACK
|
STAGING_BUFFER_TYPE_READBACK
|
||||||
};
|
};
|
||||||
|
|
||||||
// Descriptor sets
|
// Descriptor set layouts
|
||||||
enum DESCRIPTOR_SET
|
enum DESCRIPTOR_SET_LAYOUT
|
||||||
{
|
{
|
||||||
DESCRIPTOR_SET_UNIFORM_BUFFERS,
|
DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS,
|
||||||
DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS,
|
DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS,
|
||||||
DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS,
|
DESCRIPTOR_SET_LAYOUT_SHADER_STORAGE_BUFFERS,
|
||||||
NUM_DESCRIPTOR_SETS
|
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
|
// 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 INITIAL_UNIFORM_STREAM_BUFFER_SIZE = 16 * 1024 * 1024;
|
||||||
constexpr size_t MAXIMUM_UNIFORM_STREAM_BUFFER_SIZE = 32 * 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
|
// Push constant buffer size for utility shaders
|
||||||
constexpr u32 PUSH_CONSTANT_BUFFER_SIZE = 128;
|
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
|
// Rasterization state info
|
||||||
union RasterizationState {
|
union RasterizationState {
|
||||||
BitField<0, 2, VkCullModeFlags> cull_mode;
|
BitField<0, 2, VkCullModeFlags> cull_mode;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "VideoBackends/Vulkan/StateTracker.h"
|
#include "VideoBackends/Vulkan/StateTracker.h"
|
||||||
#include "VideoBackends/Vulkan/StreamBuffer.h"
|
#include "VideoBackends/Vulkan/StreamBuffer.h"
|
||||||
#include "VideoBackends/Vulkan/Texture2D.h"
|
#include "VideoBackends/Vulkan/Texture2D.h"
|
||||||
|
#include "VideoBackends/Vulkan/TextureConverter.h"
|
||||||
#include "VideoBackends/Vulkan/Util.h"
|
#include "VideoBackends/Vulkan/Util.h"
|
||||||
#include "VideoBackends/Vulkan/VertexFormat.h"
|
#include "VideoBackends/Vulkan/VertexFormat.h"
|
||||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||||
|
@ -182,24 +183,12 @@ bool FramebufferManager::CreateEFBRenderPass()
|
||||||
VK_ATTACHMENT_STORE_OP_STORE,
|
VK_ATTACHMENT_STORE_OP_STORE,
|
||||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||||
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||||
VK_IMAGE_LAYOUT_UNDEFINED,
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_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;
|
subpass_description.pDepthStencilAttachment = nullptr;
|
||||||
pass_info.pAttachments = &resolve_attachment;
|
pass_info.pAttachments = &resolve_attachment;
|
||||||
pass_info.attachmentCount = 1;
|
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,
|
res = vkCreateRenderPass(g_vulkan_context->GetDevice(), &pass_info, nullptr,
|
||||||
&m_depth_resolve_render_pass);
|
&m_depth_resolve_render_pass);
|
||||||
|
|
||||||
|
@ -442,8 +431,8 @@ void FramebufferManager::ReinterpretPixelData(int convtype)
|
||||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
|
||||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
g_object_cache->GetStandardPipelineLayout(), m_efb_load_render_pass,
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
|
||||||
g_object_cache->GetScreenQuadVertexShader(),
|
m_efb_load_render_pass, g_object_cache->GetScreenQuadVertexShader(),
|
||||||
g_object_cache->GetScreenQuadGeometryShader(), pixel_shader);
|
g_object_cache->GetScreenQuadGeometryShader(), pixel_shader);
|
||||||
|
|
||||||
RasterizationState rs_state = Util::GetNoCullRasterizationState();
|
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.
|
// Draw using resolve shader to write the minimum depth of all samples to the resolve texture.
|
||||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
g_object_cache->GetStandardPipelineLayout(), m_depth_resolve_render_pass,
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
|
||||||
g_object_cache->GetScreenQuadVertexShader(),
|
m_depth_resolve_render_pass, g_object_cache->GetScreenQuadVertexShader(),
|
||||||
g_object_cache->GetScreenQuadGeometryShader(), m_ps_depth_resolve);
|
g_object_cache->GetScreenQuadGeometryShader(), m_ps_depth_resolve);
|
||||||
draw.BeginRenderPass(m_depth_resolve_framebuffer, region);
|
draw.BeginRenderPass(m_depth_resolve_framebuffer, region);
|
||||||
draw.SetPSSampler(0, m_efb_depth_texture->GetView(), g_object_cache->GetPointSampler());
|
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)
|
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(),
|
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
g_object_cache->GetStandardPipelineLayout(), m_copy_color_render_pass,
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
|
||||||
g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE,
|
m_copy_color_render_pass, g_object_cache->GetScreenQuadVertexShader(),
|
||||||
m_copy_color_shader);
|
VK_NULL_HANDLE, m_copy_color_shader);
|
||||||
|
|
||||||
VkRect2D rect = {{0, 0}, {EFB_WIDTH, EFB_HEIGHT}};
|
VkRect2D rect = {{0, 0}, {EFB_WIDTH, EFB_HEIGHT}};
|
||||||
draw.BeginRenderPass(m_color_copy_framebuffer, rect);
|
draw.BeginRenderPass(m_color_copy_framebuffer, rect);
|
||||||
|
@ -719,7 +711,6 @@ bool FramebufferManager::PopulateColorReadbackTexture()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use this as a source texture now.
|
// 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();
|
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)
|
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(),
|
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
g_object_cache->GetStandardPipelineLayout(), m_copy_depth_render_pass,
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
|
||||||
g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE,
|
m_copy_depth_render_pass, g_object_cache->GetScreenQuadVertexShader(),
|
||||||
m_copy_depth_shader);
|
VK_NULL_HANDLE, m_copy_depth_shader);
|
||||||
|
|
||||||
VkRect2D rect = {{0, 0}, {EFB_WIDTH, EFB_HEIGHT}};
|
VkRect2D rect = {{0, 0}, {EFB_WIDTH, EFB_HEIGHT}};
|
||||||
draw.BeginRenderPass(m_depth_copy_framebuffer, rect);
|
draw.BeginRenderPass(m_depth_copy_framebuffer, rect);
|
||||||
|
@ -800,7 +794,6 @@ bool FramebufferManager::PopulateDepthReadbackTexture()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use this as a source texture now.
|
// 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_texture = m_depth_copy_texture.get();
|
||||||
src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
src_aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
}
|
}
|
||||||
|
@ -839,15 +832,15 @@ void FramebufferManager::InvalidatePeekCache()
|
||||||
bool FramebufferManager::CreateReadbackRenderPasses()
|
bool FramebufferManager::CreateReadbackRenderPasses()
|
||||||
{
|
{
|
||||||
VkAttachmentDescription copy_attachment = {
|
VkAttachmentDescription copy_attachment = {
|
||||||
0, // VkAttachmentDescriptionFlags flags
|
0, // VkAttachmentDescriptionFlags flags
|
||||||
EFB_COLOR_TEXTURE_FORMAT, // VkFormat format
|
EFB_COLOR_TEXTURE_FORMAT, // VkFormat format
|
||||||
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
|
VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples
|
||||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp loadOp
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp loadOp
|
||||||
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp
|
VK_ATTACHMENT_STORE_OP_STORE, // VkAttachmentStoreOp storeOp
|
||||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE, // VkAttachmentLoadOp stencilLoadOp
|
||||||
VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp
|
VK_ATTACHMENT_STORE_OP_DONT_CARE, // VkAttachmentStoreOp stencilStoreOp
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, // VkImageLayout initialLayout
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // VkImageLayout initialLayout
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL // VkImageLayout finalLayout
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL // VkImageLayout finalLayout
|
||||||
};
|
};
|
||||||
VkAttachmentReference copy_attachment_ref = {
|
VkAttachmentReference copy_attachment_ref = {
|
||||||
0, // uint32_t attachment
|
0, // uint32_t attachment
|
||||||
|
@ -865,14 +858,6 @@ bool FramebufferManager::CreateReadbackRenderPasses()
|
||||||
0, // uint32_t preserveAttachmentCount
|
0, // uint32_t preserveAttachmentCount
|
||||||
nullptr // const uint32_t* pPreserveAttachments
|
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 = {
|
VkRenderPassCreateInfo copy_pass = {
|
||||||
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType
|
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, // VkStructureType sType
|
||||||
nullptr, // const void* pNext
|
nullptr, // const void* pNext
|
||||||
|
@ -881,8 +866,8 @@ bool FramebufferManager::CreateReadbackRenderPasses()
|
||||||
©_attachment, // const VkAttachmentDescription* pAttachments
|
©_attachment, // const VkAttachmentDescription* pAttachments
|
||||||
1, // uint32_t subpassCount
|
1, // uint32_t subpassCount
|
||||||
©_subpass, // const VkSubpassDescription* pSubpasses
|
©_subpass, // const VkSubpassDescription* pSubpasses
|
||||||
1, // uint32_t dependencyCount
|
0, // uint32_t dependencyCount
|
||||||
©_dependency // const VkSubpassDependency* pDependencies
|
nullptr // const VkSubpassDependency* pDependencies
|
||||||
};
|
};
|
||||||
|
|
||||||
VkResult res = vkCreateRenderPass(g_vulkan_context->GetDevice(), ©_pass, nullptr,
|
VkResult res = vkCreateRenderPass(g_vulkan_context->GetDevice(), ©_pass, nullptr,
|
||||||
|
@ -1007,10 +992,8 @@ bool FramebufferManager::CreateReadbackTextures()
|
||||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
|
||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_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.
|
m_depth_readback_texture = StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH,
|
||||||
// TODO: Investigate if vkCmdBlitImage can be used. The documentation isn't that clear.
|
EFB_HEIGHT, EFB_DEPTH_TEXTURE_FORMAT);
|
||||||
m_depth_readback_texture = StagingTexture2DBuffer::Create(STAGING_BUFFER_TYPE_READBACK, EFB_WIDTH,
|
|
||||||
EFB_HEIGHT, EFB_DEPTH_TEXTURE_FORMAT);
|
|
||||||
if (!m_depth_copy_texture || !m_depth_readback_texture)
|
if (!m_depth_copy_texture || !m_depth_readback_texture)
|
||||||
{
|
{
|
||||||
ERROR_LOG(VIDEO, "Failed to create EFB depth readback texture");
|
ERROR_LOG(VIDEO, "Failed to create EFB depth readback texture");
|
||||||
|
@ -1024,12 +1007,6 @@ bool FramebufferManager::CreateReadbackTextures()
|
||||||
return false;
|
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;
|
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.
|
// We don't use the utility shader in order to keep the vertices compact.
|
||||||
PipelineInfo pipeline_info = {};
|
PipelineInfo pipeline_info = {};
|
||||||
pipeline_info.vertex_format = m_poke_vertex_format.get();
|
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.vs = m_poke_vertex_shader;
|
||||||
pipeline_info.gs = (m_efb_layers > 1) ? m_poke_geometry_shader : VK_NULL_HANDLE;
|
pipeline_info.gs = (m_efb_layers > 1) ? m_poke_geometry_shader : VK_NULL_HANDLE;
|
||||||
pipeline_info.ps = m_poke_fragment_shader;
|
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
|
// The destination stride can differ from the copy region width, in which case the pixels
|
||||||
// outside the copy region should not be written to.
|
// 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,
|
xfb_ptr, static_cast<u32>(source_rc.GetWidth()), fb_stride, fb_height, src_texture,
|
||||||
scaled_rc);
|
scaled_rc);
|
||||||
|
|
||||||
|
@ -1439,8 +1416,8 @@ void XFBSource::DecodeToTexture(u32 xfb_addr, u32 fb_width, u32 fb_height)
|
||||||
// Guest memory -> GPU EFB Textures
|
// Guest memory -> GPU EFB Textures
|
||||||
const u8* src_ptr = Memory::GetPointer(xfb_addr);
|
const u8* src_ptr = Memory::GetPointer(xfb_addr);
|
||||||
_assert_(src_ptr);
|
_assert_(src_ptr);
|
||||||
TextureCache::GetInstance()->DecodeYUYVTextureFromMemory(m_texture.get(), src_ptr, fb_width,
|
TextureCache::GetInstance()->GetTextureConverter()->DecodeYUYVTextureFromMemory(
|
||||||
fb_width * 2, fb_height);
|
m_texture.get(), src_ptr, fb_width, fb_width * 2, fb_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void XFBSource::CopyEFB(float gamma)
|
void XFBSource::CopyEFB(float gamma)
|
||||||
|
|
|
@ -695,15 +695,21 @@ bool ObjectCache::CreateDescriptorSetLayouts()
|
||||||
static const VkDescriptorSetLayoutBinding ssbo_set_bindings[] = {
|
static const VkDescriptorSetLayoutBinding ssbo_set_bindings[] = {
|
||||||
{0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT}};
|
{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,
|
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||||
static_cast<u32>(ArraySize(ubo_set_bindings)), ubo_set_bindings},
|
static_cast<u32>(ArraySize(ubo_set_bindings)), ubo_set_bindings},
|
||||||
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
|
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||||
static_cast<u32>(ArraySize(sampler_set_bindings)), sampler_set_bindings},
|
static_cast<u32>(ArraySize(sampler_set_bindings)), sampler_set_bindings},
|
||||||
{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, nullptr, 0,
|
{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],
|
VkResult res = vkCreateDescriptorSetLayout(g_vulkan_context->GetDevice(), &create_infos[i],
|
||||||
nullptr, &m_descriptor_set_layouts[i]);
|
nullptr, &m_descriptor_set_layouts[i]);
|
||||||
|
@ -732,47 +738,46 @@ bool ObjectCache::CreatePipelineLayouts()
|
||||||
|
|
||||||
// Descriptor sets for each pipeline layout
|
// Descriptor sets for each pipeline layout
|
||||||
VkDescriptorSetLayout standard_sets[] = {
|
VkDescriptorSetLayout standard_sets[] = {
|
||||||
m_descriptor_set_layouts[DESCRIPTOR_SET_UNIFORM_BUFFERS],
|
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS],
|
||||||
m_descriptor_set_layouts[DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS]};
|
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS]};
|
||||||
VkDescriptorSetLayout bbox_sets[] = {
|
VkDescriptorSetLayout bbox_sets[] = {
|
||||||
m_descriptor_set_layouts[DESCRIPTOR_SET_UNIFORM_BUFFERS],
|
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS],
|
||||||
m_descriptor_set_layouts[DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS],
|
m_descriptor_set_layouts[DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS],
|
||||||
m_descriptor_set_layouts[DESCRIPTOR_SET_SHADER_STORAGE_BUFFERS]};
|
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 = {
|
VkPushConstantRange push_constant_range = {
|
||||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, PUSH_CONSTANT_BUFFER_SIZE};
|
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, PUSH_CONSTANT_BUFFER_SIZE};
|
||||||
|
|
||||||
// Info for each pipeline layout
|
// Info for each pipeline layout
|
||||||
VkPipelineLayoutCreateInfo standard_info = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
VkPipelineLayoutCreateInfo pipeline_layout_info[NUM_PIPELINE_LAYOUTS] = {
|
||||||
nullptr,
|
// Standard
|
||||||
0,
|
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||||
static_cast<u32>(ArraySize(standard_sets)),
|
static_cast<u32>(ArraySize(standard_sets)), standard_sets, 0, nullptr},
|
||||||
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};
|
|
||||||
|
|
||||||
if ((res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &standard_info, nullptr,
|
// BBox
|
||||||
&m_standard_pipeline_layout)) != VK_SUCCESS ||
|
{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, nullptr, 0,
|
||||||
(res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &bbox_info, nullptr,
|
static_cast<u32>(ArraySize(bbox_sets)), bbox_sets, 0, nullptr},
|
||||||
&m_bbox_pipeline_layout)) != VK_SUCCESS ||
|
|
||||||
(res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &push_constant_info, nullptr,
|
// Push Constant
|
||||||
&m_push_constant_pipeline_layout)))
|
{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: ");
|
if ((res = vkCreatePipelineLayout(g_vulkan_context->GetDevice(), &pipeline_layout_info[i],
|
||||||
return false;
|
nullptr, &m_pipeline_layouts[i])) != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
LOG_VULKAN_ERROR(res, "vkCreatePipelineLayout failed: ");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -780,13 +785,11 @@ bool ObjectCache::CreatePipelineLayouts()
|
||||||
|
|
||||||
void ObjectCache::DestroyPipelineLayouts()
|
void ObjectCache::DestroyPipelineLayouts()
|
||||||
{
|
{
|
||||||
if (m_standard_pipeline_layout != VK_NULL_HANDLE)
|
for (VkPipelineLayout layout : m_pipeline_layouts)
|
||||||
vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_standard_pipeline_layout, nullptr);
|
{
|
||||||
if (m_bbox_pipeline_layout != VK_NULL_HANDLE)
|
if (layout != VK_NULL_HANDLE)
|
||||||
vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_bbox_pipeline_layout, nullptr);
|
vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), layout, nullptr);
|
||||||
if (m_push_constant_pipeline_layout != VK_NULL_HANDLE)
|
}
|
||||||
vkDestroyPipelineLayout(g_vulkan_context->GetDevice(), m_push_constant_pipeline_layout,
|
|
||||||
nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObjectCache::CreateUtilityShaderVertexFormat()
|
bool ObjectCache::CreateUtilityShaderVertexFormat()
|
||||||
|
|
|
@ -62,25 +62,16 @@ public:
|
||||||
ObjectCache();
|
ObjectCache();
|
||||||
~ObjectCache();
|
~ObjectCache();
|
||||||
|
|
||||||
// We have four shared pipeline layouts:
|
// Descriptor set layout accessor. Used for allocating descriptor sets.
|
||||||
// - Standard
|
VkDescriptorSetLayout GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT layout) const
|
||||||
// - 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
|
|
||||||
{
|
{
|
||||||
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
|
// Shared utility shader resources
|
||||||
VertexFormat* GetUtilityShaderVertexFormat() const
|
VertexFormat* GetUtilityShaderVertexFormat() const
|
||||||
{
|
{
|
||||||
|
@ -157,11 +148,8 @@ private:
|
||||||
void DestroySharedShaders();
|
void DestroySharedShaders();
|
||||||
void DestroySamplers();
|
void DestroySamplers();
|
||||||
|
|
||||||
std::array<VkDescriptorSetLayout, NUM_DESCRIPTOR_SETS> m_descriptor_set_layouts = {};
|
std::array<VkDescriptorSetLayout, NUM_DESCRIPTOR_SET_LAYOUTS> m_descriptor_set_layouts = {};
|
||||||
|
std::array<VkPipelineLayout, NUM_PIPELINE_LAYOUTS> m_pipeline_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::unique_ptr<VertexFormat> m_utility_shader_vertex_format;
|
std::unique_ptr<VertexFormat> m_utility_shader_vertex_format;
|
||||||
std::unique_ptr<StreamBuffer> m_utility_shader_vertex_buffer;
|
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;
|
return;
|
||||||
|
|
||||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
g_object_cache->GetPushConstantPipelineLayout(), render_pass,
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT),
|
||||||
m_vertex_shader, VK_NULL_HANDLE, m_fragment_shader);
|
render_pass, m_vertex_shader, VK_NULL_HANDLE, m_fragment_shader);
|
||||||
|
|
||||||
UtilityShaderVertex* vertices =
|
UtilityShaderVertex* vertices =
|
||||||
draw.ReserveVertices(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, text.length() * 6);
|
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
|
// No need to start a new render pass, but we do need to restore viewport state
|
||||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
g_object_cache->GetStandardPipelineLayout(),
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
|
||||||
FramebufferManager::GetInstance()->GetEFBLoadRenderPass(),
|
FramebufferManager::GetInstance()->GetEFBLoadRenderPass(),
|
||||||
g_object_cache->GetPassthroughVertexShader(),
|
g_object_cache->GetPassthroughVertexShader(),
|
||||||
g_object_cache->GetPassthroughGeometryShader(), m_clear_fragment_shader);
|
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)
|
for (u32 i = 0; i < xfb_count; ++i)
|
||||||
{
|
{
|
||||||
const XFBSource* xfb_source = static_cast<const XFBSource*>(xfb_sources[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 source_rect = xfb_source->sourceRc;
|
||||||
TargetRectangle draw_rect;
|
TargetRectangle draw_rect;
|
||||||
|
|
||||||
|
@ -646,6 +649,9 @@ void Renderer::DrawRealXFB(VkRenderPass render_pass, const TargetRectangle& targ
|
||||||
for (u32 i = 0; i < xfb_count; ++i)
|
for (u32 i = 0; i < xfb_count; ++i)
|
||||||
{
|
{
|
||||||
const XFBSource* xfb_source = static_cast<const XFBSource*>(xfb_sources[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 source_rect = xfb_source->sourceRc;
|
||||||
TargetRectangle draw_rect = target_rect;
|
TargetRectangle draw_rect = target_rect;
|
||||||
source_rect.right -= fb_stride - fb_width;
|
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
|
// Set up common data
|
||||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
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,
|
g_object_cache->GetPassthroughVertexShader(), VK_NULL_HANDLE,
|
||||||
m_blit_fragment_shader);
|
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 UBO_BINDING(packing, x) layout(packing, set = 0, binding = (x - 1))
|
||||||
#define SAMPLER_BINDING(x) layout(set = 1, binding = x)
|
#define SAMPLER_BINDING(x) layout(set = 1, binding = x)
|
||||||
#define SSBO_BINDING(x) layout(set = 2, 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 VARYING_LOCATION(x) layout(location = x)
|
||||||
#define FORCE_EARLY_Z layout(early_fragment_tests) in
|
#define FORCE_EARLY_Z layout(early_fragment_tests) in
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,9 @@ void StagingBuffer::InvalidateGPUCache(VkCommandBuffer command_buffer,
|
||||||
VkPipelineStageFlagBits dest_pipeline_stage,
|
VkPipelineStageFlagBits dest_pipeline_stage,
|
||||||
VkDeviceSize offset, VkDeviceSize size)
|
VkDeviceSize offset, VkDeviceSize size)
|
||||||
{
|
{
|
||||||
|
if (m_coherent)
|
||||||
|
return;
|
||||||
|
|
||||||
_assert_((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE));
|
_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,
|
Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_HOST_WRITE_BIT, dest_access_flags,
|
||||||
offset, size, VK_PIPELINE_STAGE_HOST_BIT, dest_pipeline_stage);
|
offset, size, VK_PIPELINE_STAGE_HOST_BIT, dest_pipeline_stage);
|
||||||
|
@ -90,6 +93,9 @@ void StagingBuffer::PrepareForGPUWrite(VkCommandBuffer command_buffer,
|
||||||
VkPipelineStageFlagBits dst_pipeline_stage,
|
VkPipelineStageFlagBits dst_pipeline_stage,
|
||||||
VkDeviceSize offset, VkDeviceSize size)
|
VkDeviceSize offset, VkDeviceSize size)
|
||||||
{
|
{
|
||||||
|
if (m_coherent)
|
||||||
|
return;
|
||||||
|
|
||||||
_assert_((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE));
|
_assert_((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE));
|
||||||
Util::BufferMemoryBarrier(command_buffer, m_buffer, 0, dst_access_flags, offset, size,
|
Util::BufferMemoryBarrier(command_buffer, m_buffer, 0, dst_access_flags, offset, size,
|
||||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, dst_pipeline_stage);
|
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,
|
VkPipelineStageFlagBits src_pipeline_stage, VkDeviceSize offset,
|
||||||
VkDeviceSize size)
|
VkDeviceSize size)
|
||||||
{
|
{
|
||||||
|
if (m_coherent)
|
||||||
|
return;
|
||||||
|
|
||||||
_assert_((offset + size) <= m_size || (offset < m_size && size == VK_WHOLE_SIZE));
|
_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,
|
Util::BufferMemoryBarrier(command_buffer, m_buffer, src_access_flags, VK_ACCESS_HOST_READ_BIT,
|
||||||
offset, size, src_pipeline_stage, VK_PIPELINE_STAGE_HOST_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);
|
FlushCPUCache(offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Vulkan::StagingBuffer>
|
bool StagingBuffer::AllocateBuffer(STAGING_BUFFER_TYPE type, VkDeviceSize size,
|
||||||
StagingBuffer::Create(STAGING_BUFFER_TYPE type, VkDeviceSize size, VkBufferUsageFlags usage)
|
VkBufferUsageFlags usage, VkBuffer* out_buffer,
|
||||||
|
VkDeviceMemory* out_memory, bool* out_coherent)
|
||||||
{
|
{
|
||||||
VkBufferCreateInfo buffer_create_info = {
|
VkBufferCreateInfo buffer_create_info = {
|
||||||
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, // VkStructureType sType
|
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
|
0, // uint32_t queueFamilyIndexCount
|
||||||
nullptr // const uint32_t* pQueueFamilyIndices
|
nullptr // const uint32_t* pQueueFamilyIndices
|
||||||
};
|
};
|
||||||
VkBuffer buffer;
|
|
||||||
VkResult res =
|
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)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: ");
|
LOG_VULKAN_ERROR(res, "vkCreateBuffer failed: ");
|
||||||
return nullptr;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkMemoryRequirements requirements;
|
VkMemoryRequirements requirements;
|
||||||
vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), buffer, &requirements);
|
vkGetBufferMemoryRequirements(g_vulkan_context->GetDevice(), *out_buffer, &requirements);
|
||||||
|
|
||||||
bool is_coherent;
|
|
||||||
u32 type_index;
|
u32 type_index;
|
||||||
if (type == STAGING_BUFFER_TYPE_UPLOAD)
|
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
|
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 = {
|
VkMemoryAllocateInfo memory_allocate_info = {
|
||||||
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, // VkStructureType sType
|
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
|
requirements.size, // VkDeviceSize allocationSize
|
||||||
type_index // uint32_t memoryTypeIndex
|
type_index // uint32_t memoryTypeIndex
|
||||||
};
|
};
|
||||||
VkDeviceMemory memory;
|
res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, out_memory);
|
||||||
res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory);
|
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: ");
|
LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: ");
|
||||||
vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr);
|
vkDestroyBuffer(g_vulkan_context->GetDevice(), *out_buffer, nullptr);
|
||||||
return 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)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkBindBufferMemory failed: ");
|
LOG_VULKAN_ERROR(res, "vkBindBufferMemory failed: ");
|
||||||
vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr);
|
vkDestroyBuffer(g_vulkan_context->GetDevice(), *out_buffer, nullptr);
|
||||||
vkFreeMemory(g_vulkan_context->GetDevice(), memory, nullptr);
|
vkFreeMemory(g_vulkan_context->GetDevice(), *out_memory, nullptr);
|
||||||
return 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
|
} // namespace Vulkan
|
||||||
|
|
|
@ -16,7 +16,7 @@ class StagingBuffer
|
||||||
public:
|
public:
|
||||||
StagingBuffer(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize size,
|
StagingBuffer(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize size,
|
||||||
bool coherent);
|
bool coherent);
|
||||||
~StagingBuffer();
|
virtual ~StagingBuffer();
|
||||||
|
|
||||||
STAGING_BUFFER_TYPE GetType() const { return m_type; }
|
STAGING_BUFFER_TYPE GetType() const { return m_type; }
|
||||||
VkDeviceSize GetSize() const { return m_size; }
|
VkDeviceSize GetSize() const { return m_size; }
|
||||||
|
@ -33,6 +33,7 @@ public:
|
||||||
void FlushCPUCache(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE);
|
void FlushCPUCache(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE);
|
||||||
|
|
||||||
// Upload part 2: Prepare for device read from the GPU side
|
// 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,
|
void InvalidateGPUCache(VkCommandBuffer command_buffer, VkAccessFlagBits dst_access_flags,
|
||||||
VkPipelineStageFlagBits dst_pipeline_stage, VkDeviceSize offset = 0,
|
VkPipelineStageFlagBits dst_pipeline_stage, VkDeviceSize offset = 0,
|
||||||
VkDeviceSize size = VK_WHOLE_SIZE);
|
VkDeviceSize size = VK_WHOLE_SIZE);
|
||||||
|
@ -59,6 +60,10 @@ public:
|
||||||
VkBufferUsageFlags usage);
|
VkBufferUsageFlags usage);
|
||||||
|
|
||||||
protected:
|
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;
|
STAGING_BUFFER_TYPE m_type;
|
||||||
VkBuffer m_buffer;
|
VkBuffer m_buffer;
|
||||||
VkDeviceMemory m_memory;
|
VkDeviceMemory m_memory;
|
||||||
|
|
|
@ -14,16 +14,16 @@
|
||||||
|
|
||||||
namespace Vulkan
|
namespace Vulkan
|
||||||
{
|
{
|
||||||
StagingTexture2D::StagingTexture2D(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format,
|
StagingTexture2D::StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory,
|
||||||
u32 stride)
|
VkDeviceSize size, bool coherent, u32 width, u32 height,
|
||||||
: m_type(type), m_width(width), m_height(height), m_format(format),
|
VkFormat format, u32 stride)
|
||||||
m_texel_size(Util::GetTexelSize(format)), m_row_stride(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()
|
StagingTexture2D::~StagingTexture2D()
|
||||||
{
|
{
|
||||||
_assert_(!m_map_pointer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StagingTexture2D::ReadTexel(u32 x, u32 y, void* data, size_t data_size) const
|
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,
|
void StagingTexture2D::CopyFromImage(VkCommandBuffer command_buffer, VkImage image,
|
||||||
u32 height, VkFormat format)
|
VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width,
|
||||||
{
|
u32 height, u32 level, u32 layer)
|
||||||
// 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, ©_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, ©_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)
|
|
||||||
{
|
{
|
||||||
// Issue the image->buffer copy.
|
// Issue the image->buffer copy.
|
||||||
VkBufferImageCopy image_copy = {
|
VkBufferImageCopy image_copy = {
|
||||||
0, // VkDeviceSize bufferOffset
|
y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset
|
||||||
m_width, // uint32_t bufferRowLength
|
m_width, // uint32_t bufferRowLength
|
||||||
0, // uint32_t bufferImageHeight
|
0, // uint32_t bufferImageHeight
|
||||||
{src_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource
|
{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,
|
vkCmdCopyImageToBuffer(command_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, m_buffer, 1,
|
||||||
&image_copy);
|
&image_copy);
|
||||||
|
|
||||||
// Ensure the write has completed.
|
// Flush CPU and GPU caches if not coherent mapping.
|
||||||
VkDeviceSize copy_size = m_row_stride * height;
|
VkDeviceSize buffer_flush_offset = y * m_row_stride;
|
||||||
Util::BufferMemoryBarrier(command_buffer, m_buffer, VK_ACCESS_TRANSFER_WRITE_BIT,
|
VkDeviceSize buffer_flush_size = height * m_row_stride;
|
||||||
VK_ACCESS_HOST_READ_BIT, 0, copy_size, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
FlushGPUCache(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
VK_PIPELINE_STAGE_HOST_BIT);
|
buffer_flush_offset, buffer_flush_size);
|
||||||
|
InvalidateCPUCache(buffer_flush_offset, buffer_flush_size);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StagingTexture2DBuffer::CopyToImage(VkCommandBuffer command_buffer, VkImage image,
|
void StagingTexture2D::CopyToImage(VkCommandBuffer command_buffer, VkImage image,
|
||||||
VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width,
|
VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width,
|
||||||
u32 height, u32 level, u32 layer)
|
u32 height, u32 level, u32 layer)
|
||||||
{
|
{
|
||||||
// If we're still mapped, flush the mapped range
|
// Flush CPU and GPU caches if not coherent mapping.
|
||||||
if (m_map_pointer && !m_coherent)
|
VkDeviceSize buffer_flush_offset = y * m_row_stride;
|
||||||
{
|
VkDeviceSize buffer_flush_size = height * m_row_stride;
|
||||||
VkMappedMemoryRange range = {VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, nullptr, m_memory,
|
FlushCPUCache(buffer_flush_offset, buffer_flush_size);
|
||||||
m_map_offset, m_map_size};
|
InvalidateGPUCache(command_buffer, VK_ACCESS_HOST_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
vkFlushMappedMemoryRanges(g_vulkan_context->GetDevice(), 1, &range);
|
buffer_flush_offset, buffer_flush_size);
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure writes are visible to GPU.
|
// Issue the buffer->image copy.
|
||||||
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
|
|
||||||
VkBufferImageCopy image_copy = {
|
VkBufferImageCopy image_copy = {
|
||||||
0, // VkDeviceSize bufferOffset
|
y * m_row_stride + x * m_texel_size, // VkDeviceSize bufferOffset
|
||||||
m_width, // uint32_t bufferRowLength
|
m_width, // uint32_t bufferRowLength
|
||||||
0, // uint32_t bufferImageHeight
|
0, // uint32_t bufferImageHeight
|
||||||
{dst_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource
|
{dst_aspect, level, layer, 1}, // VkImageSubresourceLayers imageSubresource
|
||||||
|
@ -428,109 +144,21 @@ void StagingTexture2DBuffer::CopyToImage(VkCommandBuffer command_buffer, VkImage
|
||||||
&image_copy);
|
&image_copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StagingTexture2DBuffer::Map(VkDeviceSize offset /* = 0 */,
|
std::unique_ptr<StagingTexture2D> StagingTexture2D::Create(STAGING_BUFFER_TYPE type, u32 width,
|
||||||
VkDeviceSize size /* = VK_WHOLE_SIZE */)
|
u32 height, VkFormat format)
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
// Assume tight packing.
|
// Assume tight packing.
|
||||||
u32 row_stride = Util::GetTexelSize(format) * width;
|
u32 stride = Util::GetTexelSize(format) * width;
|
||||||
u32 buffer_size = row_stride * height;
|
u32 size = stride * height;
|
||||||
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
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;
|
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;
|
VkDeviceMemory memory;
|
||||||
res = vkAllocateMemory(g_vulkan_context->GetDevice(), &memory_allocate_info, nullptr, &memory);
|
bool coherent;
|
||||||
if (res != VK_SUCCESS)
|
if (!AllocateBuffer(type, size, usage, &buffer, &memory, &coherent))
|
||||||
{
|
|
||||||
LOG_VULKAN_ERROR(res, "vkAllocateMemory failed: ");
|
|
||||||
vkDestroyBuffer(g_vulkan_context->GetDevice(), buffer, nullptr);
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
res = vkBindBufferMemory(g_vulkan_context->GetDevice(), buffer, memory, 0);
|
return std::make_unique<StagingTexture2D>(type, buffer, memory, size, coherent, width, height,
|
||||||
if (res != VK_SUCCESS)
|
format, stride);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -9,29 +9,26 @@
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "VideoBackends/Vulkan/Constants.h"
|
#include "VideoBackends/Vulkan/Constants.h"
|
||||||
|
#include "VideoBackends/Vulkan/StagingBuffer.h"
|
||||||
|
|
||||||
namespace Vulkan
|
namespace Vulkan
|
||||||
{
|
{
|
||||||
class StagingTexture2D
|
class StagingTexture2D final : public StagingBuffer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StagingTexture2D(STAGING_BUFFER_TYPE type, u32 width, u32 height, VkFormat format, u32 stride);
|
StagingTexture2D(STAGING_BUFFER_TYPE type, VkBuffer buffer, VkDeviceMemory memory,
|
||||||
virtual ~StagingTexture2D();
|
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 GetWidth() const { return m_width; }
|
||||||
u32 GetHeight() const { return m_height; }
|
u32 GetHeight() const { return m_height; }
|
||||||
VkFormat GetFormat() const { return m_format; }
|
VkFormat GetFormat() const { return m_format; }
|
||||||
u32 GetRowStride() const { return m_row_stride; }
|
u32 GetRowStride() const { return m_row_stride; }
|
||||||
u32 GetTexelSize() const { return m_texel_size; }
|
u32 GetTexelSize() const { return m_texel_size; }
|
||||||
bool IsMapped() const { return m_map_pointer != nullptr; }
|
// Requires Map() to be called first.
|
||||||
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; }
|
|
||||||
const char* GetRowPointer(u32 row) const { return m_map_pointer + row * m_row_stride; }
|
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; }
|
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 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 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;
|
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.
|
// Assumes that image is in TRANSFER_SRC layout.
|
||||||
// Results are not ready until command_buffer has been executed.
|
// Results are not ready until command_buffer has been executed.
|
||||||
virtual void CopyFromImage(VkCommandBuffer command_buffer, VkImage image,
|
void CopyFromImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags src_aspect,
|
||||||
VkImageAspectFlags src_aspect, u32 x, u32 y, u32 width, u32 height,
|
u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer);
|
||||||
u32 level, u32 layer) = 0;
|
|
||||||
|
|
||||||
// Assumes that image is in TRANSFER_DST layout.
|
// Assumes that image is in TRANSFER_DST layout.
|
||||||
// Buffer is not safe for re-use until after command_buffer has been executed.
|
// Buffer is not safe for re-use until after command_buffer has been executed.
|
||||||
virtual void CopyToImage(VkCommandBuffer command_buffer, VkImage image,
|
void CopyToImage(VkCommandBuffer command_buffer, VkImage image, VkImageAspectFlags dst_aspect,
|
||||||
VkImageAspectFlags dst_aspect, u32 x, u32 y, u32 width, u32 height,
|
u32 x, u32 y, u32 width, u32 height, u32 level, u32 layer);
|
||||||
u32 level, u32 layer) = 0;
|
|
||||||
virtual bool Map(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) = 0;
|
|
||||||
virtual void Unmap() = 0;
|
|
||||||
|
|
||||||
// Creates the optimal format of image copy.
|
// Creates the optimal format of image copy.
|
||||||
static std::unique_ptr<StagingTexture2D> Create(STAGING_BUFFER_TYPE type, u32 width, u32 height,
|
static std::unique_ptr<StagingTexture2D> Create(STAGING_BUFFER_TYPE type, u32 width, u32 height,
|
||||||
VkFormat format);
|
VkFormat format);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
STAGING_BUFFER_TYPE m_type;
|
|
||||||
u32 m_width;
|
u32 m_width;
|
||||||
u32 m_height;
|
u32 m_height;
|
||||||
VkFormat m_format;
|
VkFormat m_format;
|
||||||
u32 m_texel_size;
|
u32 m_texel_size;
|
||||||
u32 m_row_stride;
|
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;
|
m_pipeline_state.rasterization_state.depth_clamp = VK_TRUE;
|
||||||
|
|
||||||
// BBox is disabled by default.
|
// BBox is disabled by default.
|
||||||
m_pipeline_state.pipeline_layout = g_object_cache->GetStandardPipelineLayout();
|
m_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
|
||||||
m_num_active_descriptor_sets = NUM_DESCRIPTOR_SETS - 1;
|
m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS;
|
||||||
m_bbox_enabled = false;
|
m_bbox_enabled = false;
|
||||||
|
|
||||||
// Initialize all samplers to point by default
|
// 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.
|
// vertex loader that uses this format, since we need it to create a pipeline.
|
||||||
pinfo.vertex_format = VertexFormat::GetOrCreateMatchingFormat(uid.vertex_decl);
|
pinfo.vertex_format = VertexFormat::GetOrCreateMatchingFormat(uid.vertex_decl);
|
||||||
pinfo.pipeline_layout = uid.ps_uid.GetUidData()->bounding_box ?
|
pinfo.pipeline_layout = uid.ps_uid.GetUidData()->bounding_box ?
|
||||||
g_object_cache->GetBBoxPipelineLayout() :
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX) :
|
||||||
g_object_cache->GetStandardPipelineLayout();
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
|
||||||
pinfo.vs = g_object_cache->GetVertexShaderForUid(uid.vs_uid);
|
pinfo.vs = g_object_cache->GetVertexShaderForUid(uid.vs_uid);
|
||||||
if (pinfo.vs == VK_NULL_HANDLE)
|
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
|
// Change the number of active descriptor sets, as well as the pipeline layout
|
||||||
if (enable)
|
if (enable)
|
||||||
{
|
{
|
||||||
m_pipeline_state.pipeline_layout = g_object_cache->GetBBoxPipelineLayout();
|
m_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_BBOX);
|
||||||
m_num_active_descriptor_sets = NUM_DESCRIPTOR_SETS;
|
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.
|
// 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;
|
m_dirty_flags |= DIRTY_FLAG_PS_SSBO;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_pipeline_state.pipeline_layout = g_object_cache->GetStandardPipelineLayout();
|
m_pipeline_state.pipeline_layout = g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD);
|
||||||
m_num_active_descriptor_sets = NUM_DESCRIPTOR_SETS - 1;
|
m_num_active_descriptor_sets = NUM_GX_DRAW_DESCRIPTOR_SETS;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_dirty_flags |= DIRTY_FLAG_PIPELINE | DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
|
m_dirty_flags |= DIRTY_FLAG_PIPELINE | DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
|
||||||
|
@ -731,7 +731,8 @@ bool StateTracker::Bind(bool rebind_all /*= false*/)
|
||||||
{
|
{
|
||||||
vkCmdBindDescriptorSets(
|
vkCmdBindDescriptorSets(
|
||||||
command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline_state.pipeline_layout,
|
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());
|
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);
|
u32 interval = static_cast<u32>(g_ActiveConfig.iCommandBufferExecuteInterval);
|
||||||
for (u32 draw_counter : m_cpu_accesses_this_frame)
|
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;
|
u32 draw_count = draw_counter - last_draw_counter;
|
||||||
|
if (draw_count < MINIMUM_DRAW_CALLS_PER_COMMAND_BUFFER_FOR_READBACK)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (draw_count <= interval)
|
if (draw_count <= interval)
|
||||||
{
|
{
|
||||||
u32 mid_point = draw_count / 2;
|
u32 mid_point = draw_count / 2;
|
||||||
|
@ -806,6 +812,8 @@ void StateTracker::OnEndFrame()
|
||||||
counter += interval;
|
counter += interval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
last_draw_counter = draw_counter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -921,10 +929,10 @@ bool StateTracker::UpdateDescriptorSet()
|
||||||
u32 num_writes = 0;
|
u32 num_writes = 0;
|
||||||
|
|
||||||
if (m_dirty_flags & (DIRTY_FLAG_VS_UBO | DIRTY_FLAG_GS_UBO | DIRTY_FLAG_PS_UBO) ||
|
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 =
|
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);
|
VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(layout);
|
||||||
if (set == VK_NULL_HANDLE)
|
if (set == VK_NULL_HANDLE)
|
||||||
return false;
|
return false;
|
||||||
|
@ -943,15 +951,15 @@ bool StateTracker::UpdateDescriptorSet()
|
||||||
nullptr};
|
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;
|
m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_dirty_flags & DIRTY_FLAG_PS_SAMPLERS ||
|
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 =
|
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);
|
VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(layout);
|
||||||
if (set == VK_NULL_HANDLE)
|
if (set == VK_NULL_HANDLE)
|
||||||
return false;
|
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;
|
m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_bbox_enabled &&
|
if (m_bbox_enabled &&
|
||||||
(m_dirty_flags & DIRTY_FLAG_PS_SSBO ||
|
(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 =
|
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);
|
VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(layout);
|
||||||
if (set == VK_NULL_HANDLE)
|
if (set == VK_NULL_HANDLE)
|
||||||
return false;
|
return false;
|
||||||
|
@ -999,7 +1007,7 @@ bool StateTracker::UpdateDescriptorSet()
|
||||||
&m_bindings.ps_ssbo,
|
&m_bindings.ps_ssbo,
|
||||||
nullptr};
|
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;
|
m_dirty_flags |= DIRTY_FLAG_DESCRIPTOR_SET_BINDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,6 +132,13 @@ private:
|
||||||
VkPrimitiveTopology primitive_topology;
|
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
|
enum DITRY_FLAG : u32
|
||||||
{
|
{
|
||||||
DIRTY_FLAG_VS_UBO = (1 << 0),
|
DIRTY_FLAG_VS_UBO = (1 << 0),
|
||||||
|
@ -202,7 +209,7 @@ private:
|
||||||
VkPipeline m_pipeline_object = VK_NULL_HANDLE;
|
VkPipeline m_pipeline_object = VK_NULL_HANDLE;
|
||||||
|
|
||||||
// shader bindings
|
// shader bindings
|
||||||
std::array<VkDescriptorSet, NUM_DESCRIPTOR_SETS> m_descriptor_sets = {};
|
std::array<VkDescriptorSet, NUM_DESCRIPTOR_SET_BIND_POINTS> m_descriptor_sets = {};
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
std::array<VkDescriptorBufferInfo, NUM_UBO_DESCRIPTOR_SET_BINDINGS> uniform_buffer_bindings =
|
std::array<VkDescriptorBufferInfo, NUM_UBO_DESCRIPTOR_SET_BINDINGS> uniform_buffer_bindings =
|
||||||
|
|
|
@ -17,13 +17,12 @@
|
||||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
||||||
#include "VideoBackends/Vulkan/FramebufferManager.h"
|
#include "VideoBackends/Vulkan/FramebufferManager.h"
|
||||||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
#include "VideoBackends/Vulkan/ObjectCache.h"
|
||||||
#include "VideoBackends/Vulkan/PaletteTextureConverter.h"
|
|
||||||
#include "VideoBackends/Vulkan/Renderer.h"
|
#include "VideoBackends/Vulkan/Renderer.h"
|
||||||
#include "VideoBackends/Vulkan/StagingTexture2D.h"
|
#include "VideoBackends/Vulkan/StagingTexture2D.h"
|
||||||
#include "VideoBackends/Vulkan/StateTracker.h"
|
#include "VideoBackends/Vulkan/StateTracker.h"
|
||||||
#include "VideoBackends/Vulkan/StreamBuffer.h"
|
#include "VideoBackends/Vulkan/StreamBuffer.h"
|
||||||
#include "VideoBackends/Vulkan/Texture2D.h"
|
#include "VideoBackends/Vulkan/Texture2D.h"
|
||||||
#include "VideoBackends/Vulkan/TextureEncoder.h"
|
#include "VideoBackends/Vulkan/TextureConverter.h"
|
||||||
#include "VideoBackends/Vulkan/Util.h"
|
#include "VideoBackends/Vulkan/Util.h"
|
||||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||||
|
|
||||||
|
@ -37,10 +36,8 @@ TextureCache::TextureCache()
|
||||||
|
|
||||||
TextureCache::~TextureCache()
|
TextureCache::~TextureCache()
|
||||||
{
|
{
|
||||||
if (m_initialize_render_pass != VK_NULL_HANDLE)
|
if (m_render_pass != VK_NULL_HANDLE)
|
||||||
vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_initialize_render_pass, nullptr);
|
vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_render_pass, nullptr);
|
||||||
if (m_update_render_pass != VK_NULL_HANDLE)
|
|
||||||
vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_update_render_pass, nullptr);
|
|
||||||
TextureCache::DeleteShaders();
|
TextureCache::DeleteShaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,17 +63,10 @@ bool TextureCache::Initialize()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_texture_encoder = std::make_unique<TextureEncoder>();
|
m_texture_converter = std::make_unique<TextureConverter>();
|
||||||
if (!m_texture_encoder->Initialize())
|
if (!m_texture_converter->Initialize())
|
||||||
{
|
{
|
||||||
PanicAlert("Failed to initialize texture encoder.");
|
PanicAlert("Failed to initialize texture converter");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_palette_texture_converter = std::make_unique<PaletteTextureConverter>();
|
|
||||||
if (!m_palette_texture_converter->Initialize())
|
|
||||||
{
|
|
||||||
PanicAlert("Failed to initialize palette texture converter");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,31 +84,8 @@ void TextureCache::ConvertTexture(TCacheEntryBase* base_entry, TCacheEntryBase*
|
||||||
{
|
{
|
||||||
TCacheEntry* entry = static_cast<TCacheEntry*>(base_entry);
|
TCacheEntry* entry = static_cast<TCacheEntry*>(base_entry);
|
||||||
TCacheEntry* unconverted = static_cast<TCacheEntry*>(base_unconverted);
|
TCacheEntry* unconverted = static_cast<TCacheEntry*>(base_unconverted);
|
||||||
_assert_(entry->config.rendertarget);
|
|
||||||
|
|
||||||
// EFB copies can be used as paletted textures as well. For these, we can't assume them to be
|
m_texture_converter->ConvertTexture(entry, unconverted, m_render_pass, palette, format);
|
||||||
// contain the correct data before the frame begins (when the init command buffer is executed),
|
|
||||||
// so we must convert them at the appropriate time, during the drawing command buffer.
|
|
||||||
VkCommandBuffer command_buffer;
|
|
||||||
if (unconverted->IsEfbCopy())
|
|
||||||
{
|
|
||||||
command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
|
|
||||||
StateTracker::GetInstance()->EndRenderPass();
|
|
||||||
StateTracker::GetInstance()->SetPendingRebind();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Use initialization command buffer and perform conversion before the drawing commands.
|
|
||||||
command_buffer = g_command_buffer_mgr->GetCurrentInitCommandBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_palette_texture_converter->ConvertTexture(
|
|
||||||
command_buffer, GetRenderPassForTextureUpdate(entry->GetTexture()), entry->GetFramebuffer(),
|
|
||||||
unconverted->GetTexture(), entry->config.width, entry->config.height, palette, format,
|
|
||||||
unconverted->format);
|
|
||||||
|
|
||||||
// Render pass transitions to SHADER_READ_ONLY.
|
|
||||||
entry->GetTexture()->OverrideImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsDepthCopyFormat(PEControl::PixelFormat format)
|
static bool IsDepthCopyFormat(PEControl::PixelFormat format)
|
||||||
|
@ -156,9 +123,9 @@ void TextureCache::CopyEFB(u8* dst, u32 format, u32 native_width, u32 bytes_per_
|
||||||
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||||
|
|
||||||
m_texture_encoder->EncodeTextureToRam(src_texture->GetView(), dst, format, native_width,
|
m_texture_converter->EncodeTextureToMemory(src_texture->GetView(), dst, format, native_width,
|
||||||
bytes_per_row, num_blocks_y, memory_stride, src_format,
|
bytes_per_row, num_blocks_y, memory_stride, src_format,
|
||||||
is_intensity, scale_by_half, src_rect);
|
is_intensity, scale_by_half, src_rect);
|
||||||
|
|
||||||
// Transition back to original state
|
// Transition back to original state
|
||||||
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), original_layout);
|
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), original_layout);
|
||||||
|
@ -230,11 +197,10 @@ void TextureCache::ScaleTextureRectangle(TCacheEntry* dst_texture,
|
||||||
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||||
dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
|
||||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
g_object_cache->GetStandardPipelineLayout(),
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD), m_render_pass,
|
||||||
GetRenderPassForTextureUpdate(dst_texture->GetTexture()),
|
|
||||||
g_object_cache->GetPassthroughVertexShader(),
|
g_object_cache->GetPassthroughVertexShader(),
|
||||||
g_object_cache->GetPassthroughGeometryShader(), m_copy_shader);
|
g_object_cache->GetPassthroughGeometryShader(), m_copy_shader);
|
||||||
|
|
||||||
|
@ -248,9 +214,6 @@ void TextureCache::ScaleTextureRectangle(TCacheEntry* dst_texture,
|
||||||
static_cast<int>(src_texture->GetWidth()),
|
static_cast<int>(src_texture->GetWidth()),
|
||||||
static_cast<int>(src_texture->GetHeight()));
|
static_cast<int>(src_texture->GetHeight()));
|
||||||
draw.EndRenderPass();
|
draw.EndRenderPass();
|
||||||
|
|
||||||
// Render pass transitions destination texture to SHADER_READ_ONLY.
|
|
||||||
dst_texture->GetTexture()->OverrideImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConfig& config)
|
TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntryConfig& config)
|
||||||
|
@ -278,7 +241,7 @@ TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntry
|
||||||
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
m_initialize_render_pass,
|
m_render_pass,
|
||||||
static_cast<u32>(ArraySize(framebuffer_attachments)),
|
static_cast<u32>(ArraySize(framebuffer_attachments)),
|
||||||
framebuffer_attachments,
|
framebuffer_attachments,
|
||||||
texture->GetWidth(),
|
texture->GetWidth(),
|
||||||
|
@ -308,17 +271,6 @@ TextureCacheBase::TCacheEntryBase* TextureCache::CreateTexture(const TCacheEntry
|
||||||
|
|
||||||
bool TextureCache::CreateRenderPasses()
|
bool TextureCache::CreateRenderPasses()
|
||||||
{
|
{
|
||||||
static constexpr VkAttachmentDescription initialize_attachment = {
|
|
||||||
0,
|
|
||||||
TEXTURECACHE_TEXTURE_FORMAT,
|
|
||||||
VK_SAMPLE_COUNT_1_BIT,
|
|
||||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
||||||
VK_ATTACHMENT_STORE_OP_STORE,
|
|
||||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
||||||
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL};
|
|
||||||
|
|
||||||
static constexpr VkAttachmentDescription update_attachment = {
|
static constexpr VkAttachmentDescription update_attachment = {
|
||||||
0,
|
0,
|
||||||
TEXTURECACHE_TEXTURE_FORMAT,
|
TEXTURECACHE_TEXTURE_FORMAT,
|
||||||
|
@ -327,8 +279,8 @@ bool TextureCache::CreateRenderPasses()
|
||||||
VK_ATTACHMENT_STORE_OP_STORE,
|
VK_ATTACHMENT_STORE_OP_STORE,
|
||||||
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||||
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
VK_ATTACHMENT_STORE_OP_DONT_CARE,
|
||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL};
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
||||||
|
|
||||||
static constexpr VkAttachmentReference color_attachment_reference = {
|
static constexpr VkAttachmentReference color_attachment_reference = {
|
||||||
0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};
|
||||||
|
@ -340,36 +292,6 @@ bool TextureCache::CreateRenderPasses()
|
||||||
nullptr, nullptr,
|
nullptr, nullptr,
|
||||||
0, nullptr};
|
0, nullptr};
|
||||||
|
|
||||||
static constexpr VkSubpassDependency initialize_dependancies[] = {
|
|
||||||
{VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
|
||||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_TRANSFER_WRITE_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_FRAGMENT_SHADER_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}};
|
|
||||||
|
|
||||||
static constexpr VkSubpassDependency update_dependancies[] = {
|
|
||||||
{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_FRAGMENT_SHADER_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}};
|
|
||||||
|
|
||||||
VkRenderPassCreateInfo initialize_info = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
|
||||||
nullptr,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
&initialize_attachment,
|
|
||||||
1,
|
|
||||||
&subpass_description,
|
|
||||||
static_cast<u32>(ArraySize(initialize_dependancies)),
|
|
||||||
initialize_dependancies};
|
|
||||||
|
|
||||||
VkRenderPassCreateInfo update_info = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
VkRenderPassCreateInfo update_info = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
|
@ -377,44 +299,20 @@ bool TextureCache::CreateRenderPasses()
|
||||||
&update_attachment,
|
&update_attachment,
|
||||||
1,
|
1,
|
||||||
&subpass_description,
|
&subpass_description,
|
||||||
static_cast<u32>(ArraySize(update_dependancies)),
|
0,
|
||||||
update_dependancies};
|
nullptr};
|
||||||
|
|
||||||
VkResult res = vkCreateRenderPass(g_vulkan_context->GetDevice(), &initialize_info, nullptr,
|
VkResult res =
|
||||||
&m_initialize_render_pass);
|
vkCreateRenderPass(g_vulkan_context->GetDevice(), &update_info, nullptr, &m_render_pass);
|
||||||
if (res != VK_SUCCESS)
|
if (res != VK_SUCCESS)
|
||||||
{
|
{
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateRenderPass (initialize) failed: ");
|
LOG_VULKAN_ERROR(res, "vkCreateRenderPass failed: ");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = vkCreateRenderPass(g_vulkan_context->GetDevice(), &update_info, nullptr,
|
|
||||||
&m_update_render_pass);
|
|
||||||
if (res != VK_SUCCESS)
|
|
||||||
{
|
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateRenderPass (update) failed: ");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkRenderPass TextureCache::GetRenderPassForTextureUpdate(const Texture2D* texture) const
|
|
||||||
{
|
|
||||||
// EFB copies can be re-used as part of the texture pool. If this is the case, we need to insert
|
|
||||||
// a pipeline barrier to ensure that all reads from the texture expecting the old data have
|
|
||||||
// completed before overwriting the texture's contents. New textures will be in TRANSFER_DST
|
|
||||||
// due to the clear after creation.
|
|
||||||
|
|
||||||
// These two render passes are compatible, so even though the framebuffer was created with
|
|
||||||
// the initialize render pass it's still allowed.
|
|
||||||
|
|
||||||
if (texture->GetLayout() == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
|
||||||
return m_initialize_render_pass;
|
|
||||||
else
|
|
||||||
return m_update_render_pass;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureCache::TCacheEntry::TCacheEntry(const TCacheEntryConfig& config_,
|
TextureCache::TCacheEntry::TCacheEntry(const TCacheEntryConfig& config_,
|
||||||
std::unique_ptr<Texture2D> texture,
|
std::unique_ptr<Texture2D> texture,
|
||||||
VkFramebuffer framebuffer)
|
VkFramebuffer framebuffer)
|
||||||
|
@ -437,27 +335,23 @@ void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
|
||||||
width = std::max(1u, std::min(width, m_texture->GetWidth() >> level));
|
width = std::max(1u, std::min(width, m_texture->GetWidth() >> level));
|
||||||
height = std::max(1u, std::min(height, m_texture->GetHeight() >> level));
|
height = std::max(1u, std::min(height, m_texture->GetHeight() >> level));
|
||||||
|
|
||||||
// We don't care about the existing contents of the texture, so we set the image layout to
|
// We don't care about the existing contents of the texture, so we could the image layout to
|
||||||
// VK_IMAGE_LAYOUT_UNDEFINED here. However, if this texture is being re-used from the texture
|
// VK_IMAGE_LAYOUT_UNDEFINED here. However, under section 2.2.1, Queue Operation of the Vulkan
|
||||||
// pool, it may still be in use. We assume that it's not, as non-efb-copy textures are only
|
// specification, it states:
|
||||||
// returned to the pool when the frame number is different, furthermore, we're doing this
|
//
|
||||||
// on the initialize command buffer, so a texture being re-used mid-frame would have undesirable
|
// Command buffer submissions to a single queue must always adhere to command order and
|
||||||
// effects regardless.
|
// API order, but otherwise may overlap or execute out of order.
|
||||||
VkImageMemoryBarrier barrier = {
|
//
|
||||||
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // VkStructureType sType
|
// Therefore, if a previous frame's command buffer is still sampling from this texture, and we
|
||||||
nullptr, // const void* pNext
|
// overwrite it without a pipeline barrier, a texture sample could occur in parallel with the
|
||||||
0, // VkAccessFlags srcAccessMask
|
// texture upload/copy. I'm not sure if any drivers currently take advantage of this, but we
|
||||||
VK_ACCESS_TRANSFER_WRITE_BIT, // VkAccessFlags dstAccessMask
|
// should insert an explicit pipeline barrier just in case (done by TransitionToLayout).
|
||||||
VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout oldLayout
|
//
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, // VkImageLayout newLayout
|
// We transition to TRANSFER_DST, ready for the image copy, and leave the texture in this state.
|
||||||
VK_QUEUE_FAMILY_IGNORED, // uint32_t srcQueueFamilyIndex
|
// This is so that the remaining mip levels can be uploaded without barriers, and then when the
|
||||||
VK_QUEUE_FAMILY_IGNORED, // uint32_t dstQueueFamilyIndex
|
// texture is used, it can be transitioned to SHADER_READ_ONLY (see TCacheEntry::Bind).
|
||||||
m_texture->GetImage(), // VkImage image
|
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentInitCommandBuffer(),
|
||||||
{VK_IMAGE_ASPECT_COLOR_BIT, level, 1, 0, 1}, // VkImageSubresourceRange subresourceRange
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||||
};
|
|
||||||
vkCmdPipelineBarrier(g_command_buffer_mgr->GetCurrentInitCommandBuffer(),
|
|
||||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0,
|
|
||||||
nullptr, 0, nullptr, 1, &barrier);
|
|
||||||
|
|
||||||
// Does this texture data fit within the streaming buffer?
|
// Does this texture data fit within the streaming buffer?
|
||||||
u32 upload_width = width;
|
u32 upload_width = width;
|
||||||
|
@ -540,16 +434,6 @@ void TextureCache::TCacheEntry::Load(unsigned int width, unsigned int height,
|
||||||
m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, width,
|
m_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, width,
|
||||||
height, level, 0);
|
height, level, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transition to shader read only.
|
|
||||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
|
||||||
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
||||||
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
|
||||||
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
||||||
vkCmdPipelineBarrier(g_command_buffer_mgr->GetCurrentInitCommandBuffer(),
|
|
||||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0,
|
|
||||||
nullptr, 0, nullptr, 1, &barrier);
|
|
||||||
m_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, PEControl::PixelFormat src_format,
|
void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, PEControl::PixelFormat src_format,
|
||||||
|
@ -586,11 +470,12 @@ void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, PEControl::PixelFormat
|
||||||
scale_by_half ? g_object_cache->GetLinearSampler() : g_object_cache->GetPointSampler();
|
scale_by_half ? g_object_cache->GetLinearSampler() : g_object_cache->GetPointSampler();
|
||||||
VkImageLayout original_layout = src_texture->GetLayout();
|
VkImageLayout original_layout = src_texture->GetLayout();
|
||||||
src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||||
|
m_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
|
||||||
UtilityShaderDraw draw(
|
UtilityShaderDraw draw(
|
||||||
command_buffer, g_object_cache->GetPushConstantPipelineLayout(),
|
command_buffer, g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT),
|
||||||
TextureCache::GetInstance()->GetRenderPassForTextureUpdate(m_texture.get()),
|
TextureCache::GetInstance()->m_render_pass, g_object_cache->GetPassthroughVertexShader(),
|
||||||
g_object_cache->GetPassthroughVertexShader(), g_object_cache->GetPassthroughGeometryShader(),
|
g_object_cache->GetPassthroughGeometryShader(),
|
||||||
is_depth_copy ? TextureCache::GetInstance()->m_efb_depth_to_tex_shader :
|
is_depth_copy ? TextureCache::GetInstance()->m_efb_depth_to_tex_shader :
|
||||||
TextureCache::GetInstance()->m_efb_color_to_tex_shader);
|
TextureCache::GetInstance()->m_efb_color_to_tex_shader);
|
||||||
|
|
||||||
|
@ -614,7 +499,7 @@ void TextureCache::TCacheEntry::FromRenderTarget(u8* dst, PEControl::PixelFormat
|
||||||
src_texture->TransitionToLayout(command_buffer, original_layout);
|
src_texture->TransitionToLayout(command_buffer, original_layout);
|
||||||
|
|
||||||
// Render pass transitions texture to SHADER_READ_ONLY.
|
// Render pass transitions texture to SHADER_READ_ONLY.
|
||||||
m_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
m_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureCache::TCacheEntry::CopyRectangleFromTexture(const TCacheEntryBase* source,
|
void TextureCache::TCacheEntry::CopyRectangleFromTexture(const TCacheEntryBase* source,
|
||||||
|
@ -634,6 +519,8 @@ void TextureCache::TCacheEntry::CopyRectangleFromTexture(const TCacheEntryBase*
|
||||||
|
|
||||||
void TextureCache::TCacheEntry::Bind(unsigned int stage)
|
void TextureCache::TCacheEntry::Bind(unsigned int stage)
|
||||||
{
|
{
|
||||||
|
m_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||||
StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView());
|
StateTracker::GetInstance()->SetTexture(stage, m_texture->GetView());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -757,52 +644,6 @@ bool TextureCache::CompileShaders()
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
static const char RGB_TO_YUYV_SHADER_SOURCE[] = R"(
|
|
||||||
SAMPLER_BINDING(0) uniform sampler2DArray source;
|
|
||||||
layout(location = 0) in vec3 uv0;
|
|
||||||
layout(location = 0) out vec4 ocol0;
|
|
||||||
|
|
||||||
const vec3 y_const = vec3(0.257,0.504,0.098);
|
|
||||||
const vec3 u_const = vec3(-0.148,-0.291,0.439);
|
|
||||||
const vec3 v_const = vec3(0.439,-0.368,-0.071);
|
|
||||||
const vec4 const3 = vec4(0.0625,0.5,0.0625,0.5);
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
vec3 c0 = texture(source, vec3(uv0.xy - dFdx(uv0.xy) * 0.25, 0.0)).rgb;
|
|
||||||
vec3 c1 = texture(source, vec3(uv0.xy + dFdx(uv0.xy) * 0.25, 0.0)).rgb;
|
|
||||||
vec3 c01 = (c0 + c1) * 0.5;
|
|
||||||
ocol0 = vec4(dot(c1, y_const),
|
|
||||||
dot(c01,u_const),
|
|
||||||
dot(c0,y_const),
|
|
||||||
dot(c01, v_const)) + const3;
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
static const char YUYV_TO_RGB_SHADER_SOURCE[] = R"(
|
|
||||||
SAMPLER_BINDING(0) uniform sampler2D source;
|
|
||||||
layout(location = 0) in vec3 uv0;
|
|
||||||
layout(location = 0) out vec4 ocol0;
|
|
||||||
|
|
||||||
void main()
|
|
||||||
{
|
|
||||||
ivec2 uv = ivec2(gl_FragCoord.xy);
|
|
||||||
vec4 c0 = texelFetch(source, ivec2(uv.x / 2, uv.y), 0);
|
|
||||||
|
|
||||||
// The texture used to stage the upload is in BGRA order.
|
|
||||||
c0 = c0.zyxw;
|
|
||||||
|
|
||||||
float y = mix(c0.r, c0.b, (uv.x & 1) == 1);
|
|
||||||
float yComp = 1.164 * (y - 0.0625);
|
|
||||||
float uComp = c0.g - 0.5;
|
|
||||||
float vComp = c0.a - 0.5;
|
|
||||||
ocol0 = vec4(yComp + (1.596 * vComp),
|
|
||||||
yComp - (0.813 * vComp) - (0.391 * uComp),
|
|
||||||
yComp + (2.018 * uComp),
|
|
||||||
1.0);
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
std::string header = g_object_cache->GetUtilityShaderHeader();
|
std::string header = g_object_cache->GetUtilityShaderHeader();
|
||||||
std::string source;
|
std::string source;
|
||||||
|
|
||||||
|
@ -818,14 +659,8 @@ bool TextureCache::CompileShaders()
|
||||||
source = header + EFB_DEPTH_TO_TEX_SOURCE;
|
source = header + EFB_DEPTH_TO_TEX_SOURCE;
|
||||||
m_efb_depth_to_tex_shader = Util::CompileAndCreateFragmentShader(source);
|
m_efb_depth_to_tex_shader = Util::CompileAndCreateFragmentShader(source);
|
||||||
|
|
||||||
source = header + RGB_TO_YUYV_SHADER_SOURCE;
|
return m_copy_shader != VK_NULL_HANDLE && m_efb_color_to_tex_shader != VK_NULL_HANDLE &&
|
||||||
m_rgb_to_yuyv_shader = Util::CompileAndCreateFragmentShader(source);
|
m_efb_depth_to_tex_shader != VK_NULL_HANDLE;
|
||||||
source = header + YUYV_TO_RGB_SHADER_SOURCE;
|
|
||||||
m_yuyv_to_rgb_shader = Util::CompileAndCreateFragmentShader(source);
|
|
||||||
|
|
||||||
return (m_copy_shader != VK_NULL_HANDLE && m_efb_color_to_tex_shader != VK_NULL_HANDLE &&
|
|
||||||
m_efb_depth_to_tex_shader != VK_NULL_HANDLE && m_rgb_to_yuyv_shader != VK_NULL_HANDLE &&
|
|
||||||
m_yuyv_to_rgb_shader != VK_NULL_HANDLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextureCache::DeleteShaders()
|
void TextureCache::DeleteShaders()
|
||||||
|
@ -849,120 +684,6 @@ void TextureCache::DeleteShaders()
|
||||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_efb_depth_to_tex_shader, nullptr);
|
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_efb_depth_to_tex_shader, nullptr);
|
||||||
m_efb_depth_to_tex_shader = VK_NULL_HANDLE;
|
m_efb_depth_to_tex_shader = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
if (m_rgb_to_yuyv_shader != VK_NULL_HANDLE)
|
|
||||||
{
|
|
||||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_rgb_to_yuyv_shader, nullptr);
|
|
||||||
m_rgb_to_yuyv_shader = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
if (m_yuyv_to_rgb_shader != VK_NULL_HANDLE)
|
|
||||||
{
|
|
||||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_yuyv_to_rgb_shader, nullptr);
|
|
||||||
m_yuyv_to_rgb_shader = VK_NULL_HANDLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureCache::EncodeYUYVTextureToMemory(void* dst_ptr, u32 dst_width, u32 dst_stride,
|
|
||||||
u32 dst_height, Texture2D* src_texture,
|
|
||||||
const MathUtil::Rectangle<int>& src_rect)
|
|
||||||
{
|
|
||||||
StateTracker::GetInstance()->EndRenderPass();
|
|
||||||
|
|
||||||
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
|
|
||||||
src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
||||||
|
|
||||||
// Borrow framebuffer from EFB2RAM encoder.
|
|
||||||
Texture2D* encoding_texture = m_texture_encoder->GetEncodingTexture();
|
|
||||||
StagingTexture2D* download_texture = m_texture_encoder->GetDownloadTexture();
|
|
||||||
encoding_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
|
||||||
|
|
||||||
// Use fragment shader to convert RGBA to YUYV.
|
|
||||||
// Use linear sampler for downscaling. This texture is in BGRA order, so the data is already in
|
|
||||||
// the order the guest is expecting and we don't have to swap it at readback time. The width
|
|
||||||
// is halved because we're using an RGBA8 texture, but the YUYV data is two bytes per pixel.
|
|
||||||
u32 output_width = dst_width / 2;
|
|
||||||
UtilityShaderDraw draw(command_buffer, g_object_cache->GetStandardPipelineLayout(),
|
|
||||||
m_texture_encoder->GetEncodingRenderPass(),
|
|
||||||
g_object_cache->GetPassthroughVertexShader(), VK_NULL_HANDLE,
|
|
||||||
m_rgb_to_yuyv_shader);
|
|
||||||
VkRect2D region = {{0, 0}, {output_width, dst_height}};
|
|
||||||
draw.BeginRenderPass(m_texture_encoder->GetEncodingTextureFramebuffer(), region);
|
|
||||||
draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler());
|
|
||||||
draw.DrawQuad(0, 0, static_cast<int>(output_width), static_cast<int>(dst_height), src_rect.left,
|
|
||||||
src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(),
|
|
||||||
static_cast<int>(src_texture->GetWidth()),
|
|
||||||
static_cast<int>(src_texture->GetHeight()));
|
|
||||||
draw.EndRenderPass();
|
|
||||||
|
|
||||||
// Render pass transitions to TRANSFER_SRC.
|
|
||||||
encoding_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
|
||||||
|
|
||||||
// Copy from encoding texture to download buffer.
|
|
||||||
download_texture->CopyFromImage(command_buffer, encoding_texture->GetImage(),
|
|
||||||
VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, output_width, dst_height, 0, 0);
|
|
||||||
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
|
||||||
|
|
||||||
// Finally, copy to guest memory. This may have a different stride.
|
|
||||||
download_texture->ReadTexels(0, 0, output_width, dst_height, dst_ptr, dst_stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureCache::DecodeYUYVTextureFromMemory(TCacheEntry* dst_texture, const void* src_ptr,
|
|
||||||
u32 src_width, u32 src_stride, u32 src_height)
|
|
||||||
{
|
|
||||||
// Copies (and our decoding step) cannot be done inside a render pass.
|
|
||||||
StateTracker::GetInstance()->EndRenderPass();
|
|
||||||
|
|
||||||
// We share the upload buffer with normal textures here, since the XFB buffers aren't very large.
|
|
||||||
u32 upload_size = src_stride * src_height;
|
|
||||||
if (!m_texture_upload_buffer->ReserveMemory(upload_size,
|
|
||||||
g_vulkan_context->GetBufferImageGranularity()))
|
|
||||||
{
|
|
||||||
// Execute the command buffer first.
|
|
||||||
WARN_LOG(VIDEO, "Executing command list while waiting for space in texture upload buffer");
|
|
||||||
Util::ExecuteCurrentCommandsAndRestoreState(false);
|
|
||||||
if (!m_texture_upload_buffer->ReserveMemory(upload_size,
|
|
||||||
g_vulkan_context->GetBufferImageGranularity()))
|
|
||||||
PanicAlert("Failed to allocate space in texture upload buffer");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assume that each source row is not padded.
|
|
||||||
_assert_(src_stride == (src_width * sizeof(u16)));
|
|
||||||
VkDeviceSize image_upload_buffer_offset = m_texture_upload_buffer->GetCurrentOffset();
|
|
||||||
std::memcpy(m_texture_upload_buffer->GetCurrentHostPointer(), src_ptr, upload_size);
|
|
||||||
m_texture_upload_buffer->CommitMemory(upload_size);
|
|
||||||
|
|
||||||
// Copy from the upload buffer to the intermediate texture. We borrow this from the encoder.
|
|
||||||
// The width is specified as half here because we have two pixels packed in each RGBA texel.
|
|
||||||
// In the future this could be skipped by reading the upload buffer as a uniform texel buffer.
|
|
||||||
VkBufferImageCopy image_copy = {
|
|
||||||
image_upload_buffer_offset, // VkDeviceSize bufferOffset
|
|
||||||
0, // uint32_t bufferRowLength
|
|
||||||
0, // uint32_t bufferImageHeight
|
|
||||||
{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, // VkImageSubresourceLayers imageSubresource
|
|
||||||
{0, 0, 0}, // VkOffset3D imageOffset
|
|
||||||
{src_width / 2, src_height, 1} // VkExtent3D imageExtent
|
|
||||||
};
|
|
||||||
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
|
|
||||||
Texture2D* intermediate_texture = m_texture_encoder->GetEncodingTexture();
|
|
||||||
intermediate_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
|
||||||
vkCmdCopyBufferToImage(command_buffer, m_texture_upload_buffer->GetBuffer(),
|
|
||||||
intermediate_texture->GetImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
|
|
||||||
&image_copy);
|
|
||||||
intermediate_texture->TransitionToLayout(command_buffer,
|
|
||||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
|
||||||
dst_texture->GetTexture()->TransitionToLayout(command_buffer,
|
|
||||||
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
|
||||||
|
|
||||||
// Convert from the YUYV data now in the intermediate texture to RGBA in the destination.
|
|
||||||
UtilityShaderDraw draw(command_buffer, g_object_cache->GetStandardPipelineLayout(),
|
|
||||||
m_texture_encoder->GetEncodingRenderPass(),
|
|
||||||
g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE,
|
|
||||||
m_yuyv_to_rgb_shader);
|
|
||||||
VkRect2D region = {{0, 0}, {src_width, src_height}};
|
|
||||||
draw.BeginRenderPass(dst_texture->GetFramebuffer(), region);
|
|
||||||
draw.SetViewportAndScissor(0, 0, static_cast<int>(src_width), static_cast<int>(src_height));
|
|
||||||
draw.SetPSSampler(0, intermediate_texture->GetView(), g_object_cache->GetPointSampler());
|
|
||||||
draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4);
|
|
||||||
draw.EndRenderPass();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|
|
@ -12,10 +12,9 @@
|
||||||
|
|
||||||
namespace Vulkan
|
namespace Vulkan
|
||||||
{
|
{
|
||||||
class PaletteTextureConverter;
|
class TextureConverter;
|
||||||
class StateTracker;
|
class StateTracker;
|
||||||
class Texture2D;
|
class Texture2D;
|
||||||
class TextureEncoder;
|
|
||||||
|
|
||||||
class TextureCache : public TextureCacheBase
|
class TextureCache : public TextureCacheBase
|
||||||
{
|
{
|
||||||
|
@ -49,6 +48,7 @@ public:
|
||||||
|
|
||||||
static TextureCache* GetInstance();
|
static TextureCache* GetInstance();
|
||||||
|
|
||||||
|
TextureConverter* GetTextureConverter() const { return m_texture_converter.get(); }
|
||||||
bool Initialize();
|
bool Initialize();
|
||||||
|
|
||||||
bool CompileShaders() override;
|
bool CompileShaders() override;
|
||||||
|
@ -66,17 +66,8 @@ public:
|
||||||
void CopyRectangleFromTexture(TCacheEntry* dst_texture, const MathUtil::Rectangle<int>& dst_rect,
|
void CopyRectangleFromTexture(TCacheEntry* dst_texture, const MathUtil::Rectangle<int>& dst_rect,
|
||||||
Texture2D* src_texture, const MathUtil::Rectangle<int>& src_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:
|
private:
|
||||||
bool CreateRenderPasses();
|
bool CreateRenderPasses();
|
||||||
VkRenderPass GetRenderPassForTextureUpdate(const Texture2D* texture) const;
|
|
||||||
|
|
||||||
// Copies the contents of a texture using vkCmdCopyImage
|
// Copies the contents of a texture using vkCmdCopyImage
|
||||||
void CopyTextureRectangle(TCacheEntry* dst_texture, const MathUtil::Rectangle<int>& dst_rect,
|
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,
|
void ScaleTextureRectangle(TCacheEntry* dst_texture, const MathUtil::Rectangle<int>& dst_rect,
|
||||||
Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect);
|
Texture2D* src_texture, const MathUtil::Rectangle<int>& src_rect);
|
||||||
|
|
||||||
VkRenderPass m_initialize_render_pass = VK_NULL_HANDLE;
|
VkRenderPass m_render_pass = VK_NULL_HANDLE;
|
||||||
VkRenderPass m_update_render_pass = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
std::unique_ptr<StreamBuffer> m_texture_upload_buffer;
|
std::unique_ptr<StreamBuffer> m_texture_upload_buffer;
|
||||||
|
|
||||||
std::unique_ptr<TextureEncoder> m_texture_encoder;
|
std::unique_ptr<TextureConverter> m_texture_converter;
|
||||||
|
|
||||||
std::unique_ptr<PaletteTextureConverter> m_palette_texture_converter;
|
|
||||||
|
|
||||||
VkShaderModule m_copy_shader = VK_NULL_HANDLE;
|
VkShaderModule m_copy_shader = VK_NULL_HANDLE;
|
||||||
VkShaderModule m_efb_color_to_tex_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_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
|
} // namespace Vulkan
|
||||||
|
|
|
@ -0,0 +1,675 @@
|
||||||
|
// Copyright 2016 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "VideoBackends/Vulkan/TextureConverter.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/StagingTexture2D.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"
|
||||||
|
|
||||||
|
#include "VideoCommon/TextureConversionShader.h"
|
||||||
|
#include "VideoCommon/TextureDecoder.h"
|
||||||
|
|
||||||
|
namespace Vulkan
|
||||||
|
{
|
||||||
|
TextureConverter::TextureConverter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureConverter::~TextureConverter()
|
||||||
|
{
|
||||||
|
for (const auto& it : m_palette_conversion_shaders)
|
||||||
|
{
|
||||||
|
if (it != VK_NULL_HANDLE)
|
||||||
|
vkDestroyShaderModule(g_vulkan_context->GetDevice(), it, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_texel_buffer_view_r16_uint != VK_NULL_HANDLE)
|
||||||
|
vkDestroyBufferView(g_vulkan_context->GetDevice(), m_texel_buffer_view_r16_uint, nullptr);
|
||||||
|
if (m_texel_buffer_view_rgba8_unorm != VK_NULL_HANDLE)
|
||||||
|
vkDestroyBufferView(g_vulkan_context->GetDevice(), m_texel_buffer_view_rgba8_unorm, nullptr);
|
||||||
|
|
||||||
|
if (m_encoding_render_pass != VK_NULL_HANDLE)
|
||||||
|
vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_encoding_render_pass, nullptr);
|
||||||
|
|
||||||
|
if (m_encoding_render_framebuffer != VK_NULL_HANDLE)
|
||||||
|
vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_encoding_render_framebuffer, nullptr);
|
||||||
|
|
||||||
|
for (VkShaderModule shader : m_encoding_shaders)
|
||||||
|
{
|
||||||
|
if (shader != VK_NULL_HANDLE)
|
||||||
|
vkDestroyShaderModule(g_vulkan_context->GetDevice(), shader, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_rgb_to_yuyv_shader != VK_NULL_HANDLE)
|
||||||
|
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_rgb_to_yuyv_shader, nullptr);
|
||||||
|
if (m_yuyv_to_rgb_shader != VK_NULL_HANDLE)
|
||||||
|
vkDestroyShaderModule(g_vulkan_context->GetDevice(), m_yuyv_to_rgb_shader, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureConverter::Initialize()
|
||||||
|
{
|
||||||
|
if (!CreateTexelBuffer())
|
||||||
|
{
|
||||||
|
PanicAlert("Failed to create uniform buffer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CompilePaletteConversionShaders())
|
||||||
|
{
|
||||||
|
PanicAlert("Failed to compile palette conversion shaders");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CompileEncodingShaders())
|
||||||
|
{
|
||||||
|
PanicAlert("Failed to compile texture encoding shaders");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CreateEncodingRenderPass())
|
||||||
|
{
|
||||||
|
PanicAlert("Failed to create encode render pass");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CreateEncodingTexture())
|
||||||
|
{
|
||||||
|
PanicAlert("Failed to create encoding texture");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CreateEncodingDownloadTexture())
|
||||||
|
{
|
||||||
|
PanicAlert("Failed to create download texture");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CompileYUYVConversionShaders())
|
||||||
|
{
|
||||||
|
PanicAlert("Failed to compile YUYV conversion shaders");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureConverter::ReserveTexelBufferStorage(size_t size, size_t alignment)
|
||||||
|
{
|
||||||
|
// Enforce the minimum alignment for texture buffers on the device.
|
||||||
|
size_t actual_alignment =
|
||||||
|
std::max(static_cast<size_t>(g_vulkan_context->GetTexelBufferAlignment()), alignment);
|
||||||
|
if (m_texel_buffer->ReserveMemory(size, actual_alignment))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
WARN_LOG(VIDEO, "Executing command list while waiting for space in palette buffer");
|
||||||
|
Util::ExecuteCurrentCommandsAndRestoreState(false);
|
||||||
|
|
||||||
|
// This next call should never fail, since a command buffer is now in-flight and we can
|
||||||
|
// wait on the fence for the GPU to finish. If this returns false, it's probably because
|
||||||
|
// the device has been lost, which is fatal anyway.
|
||||||
|
if (!m_texel_buffer->ReserveMemory(size, actual_alignment))
|
||||||
|
{
|
||||||
|
PanicAlert("Failed to allocate space for texture conversion");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkCommandBuffer
|
||||||
|
TextureConverter::GetCommandBufferForTextureConversion(const TextureCache::TCacheEntry* src_entry)
|
||||||
|
{
|
||||||
|
// EFB copies can be used as paletted textures as well. For these, we can't assume them to be
|
||||||
|
// contain the correct data before the frame begins (when the init command buffer is executed),
|
||||||
|
// so we must convert them at the appropriate time, during the drawing command buffer.
|
||||||
|
if (src_entry->IsEfbCopy())
|
||||||
|
{
|
||||||
|
StateTracker::GetInstance()->EndRenderPass();
|
||||||
|
StateTracker::GetInstance()->SetPendingRebind();
|
||||||
|
return g_command_buffer_mgr->GetCurrentCommandBuffer();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Use initialization command buffer and perform conversion before the drawing commands.
|
||||||
|
return g_command_buffer_mgr->GetCurrentInitCommandBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureConverter::ConvertTexture(TextureCache::TCacheEntry* dst_entry,
|
||||||
|
TextureCache::TCacheEntry* src_entry,
|
||||||
|
VkRenderPass render_pass, const void* palette,
|
||||||
|
TlutFormat palette_format)
|
||||||
|
{
|
||||||
|
struct PSUniformBlock
|
||||||
|
{
|
||||||
|
float multiplier;
|
||||||
|
int texel_buffer_offset;
|
||||||
|
int pad[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
_assert_(static_cast<size_t>(palette_format) < NUM_PALETTE_CONVERSION_SHADERS);
|
||||||
|
_assert_(dst_entry->config.rendertarget);
|
||||||
|
|
||||||
|
// We want to align to 2 bytes (R16) or the device's texel buffer alignment, whichever is greater.
|
||||||
|
size_t palette_size = (src_entry->format & 0xF) == GX_TF_I4 ? 32 : 512;
|
||||||
|
if (!ReserveTexelBufferStorage(palette_size, sizeof(u16)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Copy in palette to texel buffer.
|
||||||
|
u32 palette_offset = static_cast<u32>(m_texel_buffer->GetCurrentOffset());
|
||||||
|
memcpy(m_texel_buffer->GetCurrentHostPointer(), palette, palette_size);
|
||||||
|
m_texel_buffer->CommitMemory(palette_size);
|
||||||
|
|
||||||
|
VkCommandBuffer command_buffer = GetCommandBufferForTextureConversion(src_entry);
|
||||||
|
src_entry->GetTexture()->TransitionToLayout(command_buffer,
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||||
|
dst_entry->GetTexture()->TransitionToLayout(command_buffer,
|
||||||
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
|
||||||
|
// Bind and draw to the destination.
|
||||||
|
UtilityShaderDraw draw(command_buffer,
|
||||||
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION),
|
||||||
|
render_pass, g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE,
|
||||||
|
m_palette_conversion_shaders[palette_format]);
|
||||||
|
|
||||||
|
VkRect2D region = {{0, 0}, {dst_entry->config.width, dst_entry->config.height}};
|
||||||
|
draw.BeginRenderPass(dst_entry->GetFramebuffer(), region);
|
||||||
|
|
||||||
|
PSUniformBlock uniforms = {};
|
||||||
|
uniforms.multiplier = (src_entry->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_entry->GetTexture()->GetView(), g_object_cache->GetPointSampler());
|
||||||
|
draw.SetPSTexelBuffer(m_texel_buffer_view_r16_uint);
|
||||||
|
draw.SetViewportAndScissor(0, 0, dst_entry->config.width, dst_entry->config.height);
|
||||||
|
draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4);
|
||||||
|
draw.EndRenderPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureConverter::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& src_rect)
|
||||||
|
{
|
||||||
|
if (m_encoding_shaders[format] == VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
ERROR_LOG(VIDEO, "Missing encoding fragment shader for format %u", format);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't do our own draw within a render pass.
|
||||||
|
StateTracker::GetInstance()->EndRenderPass();
|
||||||
|
|
||||||
|
m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
|
||||||
|
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_PUSH_CONSTANT),
|
||||||
|
m_encoding_render_pass, g_object_cache->GetScreenQuadVertexShader(),
|
||||||
|
VK_NULL_HANDLE, m_encoding_shaders[format]);
|
||||||
|
|
||||||
|
// Uniform - int4 of left,top,native_width,scale
|
||||||
|
s32 position_uniform[4] = {src_rect.left, src_rect.top, static_cast<s32>(native_width),
|
||||||
|
scale_by_half ? 2 : 1};
|
||||||
|
draw.SetPushConstants(position_uniform, sizeof(position_uniform));
|
||||||
|
|
||||||
|
// Doesn't make sense to linear filter depth values
|
||||||
|
draw.SetPSSampler(0, src_texture, (scale_by_half && src_format != PEControl::Z24) ?
|
||||||
|
g_object_cache->GetLinearSampler() :
|
||||||
|
g_object_cache->GetPointSampler());
|
||||||
|
|
||||||
|
u32 render_width = bytes_per_row / sizeof(u32);
|
||||||
|
u32 render_height = num_blocks_y;
|
||||||
|
Util::SetViewportAndScissor(g_command_buffer_mgr->GetCurrentCommandBuffer(), 0, 0, render_width,
|
||||||
|
render_height);
|
||||||
|
|
||||||
|
VkRect2D render_region = {{0, 0}, {render_width, render_height}};
|
||||||
|
draw.BeginRenderPass(m_encoding_render_framebuffer, render_region);
|
||||||
|
draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4);
|
||||||
|
draw.EndRenderPass();
|
||||||
|
|
||||||
|
// Transition the image before copying
|
||||||
|
m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||||
|
m_encoding_download_texture->CopyFromImage(
|
||||||
|
g_command_buffer_mgr->GetCurrentCommandBuffer(), m_encoding_render_texture->GetImage(),
|
||||||
|
VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, render_width, render_height, 0, 0);
|
||||||
|
|
||||||
|
// Block until the GPU has finished copying to the staging texture.
|
||||||
|
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
||||||
|
|
||||||
|
// Copy from staging texture to the final destination, adjusting pitch if necessary.
|
||||||
|
m_encoding_download_texture->ReadTexels(0, 0, render_width, render_height, dest_ptr,
|
||||||
|
memory_stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureConverter::EncodeTextureToMemoryYUYV(void* dst_ptr, u32 dst_width, u32 dst_stride,
|
||||||
|
u32 dst_height, Texture2D* src_texture,
|
||||||
|
const MathUtil::Rectangle<int>& src_rect)
|
||||||
|
{
|
||||||
|
StateTracker::GetInstance()->EndRenderPass();
|
||||||
|
|
||||||
|
// Borrow framebuffer from EFB2RAM encoder.
|
||||||
|
VkCommandBuffer command_buffer = g_command_buffer_mgr->GetCurrentCommandBuffer();
|
||||||
|
src_texture->TransitionToLayout(command_buffer, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||||
|
m_encoding_render_texture->TransitionToLayout(command_buffer,
|
||||||
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
|
||||||
|
// Use fragment shader to convert RGBA to YUYV.
|
||||||
|
// Use linear sampler for downscaling. This texture is in BGRA order, so the data is already in
|
||||||
|
// the order the guest is expecting and we don't have to swap it at readback time. The width
|
||||||
|
// is halved because we're using an RGBA8 texture, but the YUYV data is two bytes per pixel.
|
||||||
|
u32 output_width = dst_width / 2;
|
||||||
|
UtilityShaderDraw draw(command_buffer,
|
||||||
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_STANDARD),
|
||||||
|
m_encoding_render_pass, g_object_cache->GetPassthroughVertexShader(),
|
||||||
|
VK_NULL_HANDLE, m_rgb_to_yuyv_shader);
|
||||||
|
VkRect2D region = {{0, 0}, {output_width, dst_height}};
|
||||||
|
draw.BeginRenderPass(m_encoding_render_framebuffer, region);
|
||||||
|
draw.SetPSSampler(0, src_texture->GetView(), g_object_cache->GetLinearSampler());
|
||||||
|
draw.DrawQuad(0, 0, static_cast<int>(output_width), static_cast<int>(dst_height), src_rect.left,
|
||||||
|
src_rect.top, 0, src_rect.GetWidth(), src_rect.GetHeight(),
|
||||||
|
static_cast<int>(src_texture->GetWidth()),
|
||||||
|
static_cast<int>(src_texture->GetHeight()));
|
||||||
|
draw.EndRenderPass();
|
||||||
|
|
||||||
|
// Render pass transitions to TRANSFER_SRC.
|
||||||
|
m_encoding_render_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
|
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||||
|
|
||||||
|
// Copy from encoding texture to download buffer.
|
||||||
|
m_encoding_download_texture->CopyFromImage(command_buffer, m_encoding_render_texture->GetImage(),
|
||||||
|
VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, output_width,
|
||||||
|
dst_height, 0, 0);
|
||||||
|
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
||||||
|
|
||||||
|
// Finally, copy to guest memory. This may have a different stride.
|
||||||
|
m_encoding_download_texture->ReadTexels(0, 0, output_width, dst_height, dst_ptr, dst_stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureConverter::DecodeYUYVTextureFromMemory(TextureCache::TCacheEntry* dst_texture,
|
||||||
|
const void* src_ptr, u32 src_width,
|
||||||
|
u32 src_stride, u32 src_height)
|
||||||
|
{
|
||||||
|
// Copies (and our decoding step) cannot be done inside a render pass.
|
||||||
|
StateTracker::GetInstance()->EndRenderPass();
|
||||||
|
StateTracker::GetInstance()->SetPendingRebind();
|
||||||
|
|
||||||
|
// Pack each row without any padding in the texel buffer.
|
||||||
|
size_t upload_stride = src_width * sizeof(u16);
|
||||||
|
size_t upload_size = upload_stride * src_height;
|
||||||
|
|
||||||
|
// Reserve space in the texel buffer for storing the raw image.
|
||||||
|
if (!ReserveTexelBufferStorage(upload_size, sizeof(u16)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Handle pitch differences here.
|
||||||
|
if (src_stride != upload_stride)
|
||||||
|
{
|
||||||
|
const u8* src_row_ptr = reinterpret_cast<const u8*>(src_ptr);
|
||||||
|
u8* dst_row_ptr = m_texel_buffer->GetCurrentHostPointer();
|
||||||
|
size_t copy_size = std::min(upload_stride, static_cast<size_t>(src_stride));
|
||||||
|
for (u32 row = 0; row < src_height; row++)
|
||||||
|
{
|
||||||
|
std::memcpy(dst_row_ptr, src_row_ptr, copy_size);
|
||||||
|
src_row_ptr += src_stride;
|
||||||
|
dst_row_ptr += upload_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::memcpy(m_texel_buffer->GetCurrentHostPointer(), src_ptr, upload_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkDeviceSize texel_buffer_offset = m_texel_buffer->GetCurrentOffset();
|
||||||
|
m_texel_buffer->CommitMemory(upload_size);
|
||||||
|
|
||||||
|
dst_texture->GetTexture()->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||||
|
|
||||||
|
// We divide the offset by 4 here because we're fetching RGBA8 elements.
|
||||||
|
// The stride is in RGBA8 elements, so we divide by two because our data is two bytes per pixel.
|
||||||
|
struct PSUniformBlock
|
||||||
|
{
|
||||||
|
int buffer_offset;
|
||||||
|
int src_stride;
|
||||||
|
};
|
||||||
|
PSUniformBlock push_constants = {static_cast<int>(texel_buffer_offset / sizeof(u32)),
|
||||||
|
static_cast<int>(src_width / 2)};
|
||||||
|
|
||||||
|
// Convert from the YUYV data now in the intermediate texture to RGBA in the destination.
|
||||||
|
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||||
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION),
|
||||||
|
m_encoding_render_pass, g_object_cache->GetScreenQuadVertexShader(),
|
||||||
|
VK_NULL_HANDLE, m_yuyv_to_rgb_shader);
|
||||||
|
VkRect2D region = {{0, 0}, {src_width, src_height}};
|
||||||
|
draw.BeginRenderPass(dst_texture->GetFramebuffer(), region);
|
||||||
|
draw.SetViewportAndScissor(0, 0, static_cast<int>(src_width), static_cast<int>(src_height));
|
||||||
|
draw.SetPSTexelBuffer(m_texel_buffer_view_rgba8_unorm);
|
||||||
|
draw.SetPushConstants(&push_constants, sizeof(push_constants));
|
||||||
|
draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4);
|
||||||
|
draw.EndRenderPass();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureConverter::CreateTexelBuffer()
|
||||||
|
{
|
||||||
|
// Prefer an 8MB buffer if possible, but use less if the device doesn't support this.
|
||||||
|
// This buffer is potentially going to be addressed as R8s in the future, so we assume
|
||||||
|
// that one element is one byte.
|
||||||
|
m_texel_buffer_size =
|
||||||
|
std::min(TEXTURE_CONVERSION_TEXEL_BUFFER_SIZE,
|
||||||
|
static_cast<size_t>(g_vulkan_context->GetDeviceLimits().maxTexelBufferElements));
|
||||||
|
|
||||||
|
m_texel_buffer = StreamBuffer::Create(VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT,
|
||||||
|
m_texel_buffer_size, m_texel_buffer_size);
|
||||||
|
if (!m_texel_buffer)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Create views of the formats that we will be using.
|
||||||
|
m_texel_buffer_view_r16_uint = CreateTexelBufferView(VK_FORMAT_R16_UINT);
|
||||||
|
m_texel_buffer_view_rgba8_unorm = CreateTexelBufferView(VK_FORMAT_R8G8B8A8_UNORM);
|
||||||
|
return m_texel_buffer_view_r16_uint != VK_NULL_HANDLE &&
|
||||||
|
m_texel_buffer_view_rgba8_unorm != VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkBufferView TextureConverter::CreateTexelBufferView(VkFormat format) const
|
||||||
|
{
|
||||||
|
// 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_texel_buffer->GetBuffer(), // VkBuffer buffer
|
||||||
|
format, // VkFormat format
|
||||||
|
0, // VkDeviceSize offset
|
||||||
|
m_texel_buffer_size // VkDeviceSize range
|
||||||
|
};
|
||||||
|
|
||||||
|
VkBufferView view;
|
||||||
|
VkResult res = vkCreateBufferView(g_vulkan_context->GetDevice(), &view_info, nullptr, &view);
|
||||||
|
if (res != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
LOG_VULKAN_ERROR(res, "vkCreateBufferView failed: ");
|
||||||
|
return VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureConverter::CompilePaletteConversionShaders()
|
||||||
|
{
|
||||||
|
static const char PALETTE_CONVERSION_FRAGMENT_SHADER_SOURCE[] = R"(
|
||||||
|
layout(std140, push_constant) uniform PCBlock
|
||||||
|
{
|
||||||
|
float multiplier;
|
||||||
|
int texture_buffer_offset;
|
||||||
|
} PC;
|
||||||
|
|
||||||
|
SAMPLER_BINDING(0) uniform sampler2DArray samp0;
|
||||||
|
TEXEL_BUFFER_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_palette_conversion_shaders[GX_TL_IA8] =
|
||||||
|
Util::CompileAndCreateFragmentShader(palette_ia8_program);
|
||||||
|
m_palette_conversion_shaders[GX_TL_RGB565] =
|
||||||
|
Util::CompileAndCreateFragmentShader(palette_rgb565_program);
|
||||||
|
m_palette_conversion_shaders[GX_TL_RGB5A3] =
|
||||||
|
Util::CompileAndCreateFragmentShader(palette_rgb5a3_program);
|
||||||
|
|
||||||
|
return m_palette_conversion_shaders[GX_TL_IA8] != VK_NULL_HANDLE &&
|
||||||
|
m_palette_conversion_shaders[GX_TL_RGB565] != VK_NULL_HANDLE &&
|
||||||
|
m_palette_conversion_shaders[GX_TL_RGB5A3] != VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureConverter::CompileEncodingShaders()
|
||||||
|
{
|
||||||
|
// Texture encoding shaders
|
||||||
|
static const u32 texture_encoding_shader_formats[] = {
|
||||||
|
GX_TF_I4, GX_TF_I8, GX_TF_IA4, GX_TF_IA8, GX_TF_RGB565, GX_TF_RGB5A3, GX_TF_RGBA8,
|
||||||
|
GX_CTF_R4, GX_CTF_RA4, GX_CTF_RA8, GX_CTF_A8, GX_CTF_R8, GX_CTF_G8, GX_CTF_B8,
|
||||||
|
GX_CTF_RG8, GX_CTF_GB8, GX_CTF_Z8H, GX_TF_Z8, GX_CTF_Z16R, GX_TF_Z16, GX_TF_Z24X8,
|
||||||
|
GX_CTF_Z4, GX_CTF_Z8M, GX_CTF_Z8L, GX_CTF_Z16L};
|
||||||
|
for (u32 format : texture_encoding_shader_formats)
|
||||||
|
{
|
||||||
|
const char* shader_source =
|
||||||
|
TextureConversionShader::GenerateEncodingShader(format, APIType::Vulkan);
|
||||||
|
m_encoding_shaders[format] = Util::CompileAndCreateFragmentShader(shader_source);
|
||||||
|
if (m_encoding_shaders[format] == VK_NULL_HANDLE)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureConverter::CreateEncodingRenderPass()
|
||||||
|
{
|
||||||
|
VkAttachmentDescription attachments[] = {
|
||||||
|
{0, ENCODING_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||||
|
VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
||||||
|
VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
|
||||||
|
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}};
|
||||||
|
|
||||||
|
VkAttachmentReference color_attachment_references[] = {
|
||||||
|
{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}};
|
||||||
|
|
||||||
|
VkSubpassDescription subpass_descriptions[] = {{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1,
|
||||||
|
color_attachment_references, nullptr, nullptr, 0,
|
||||||
|
nullptr}};
|
||||||
|
|
||||||
|
VkRenderPassCreateInfo pass_info = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
static_cast<u32>(ArraySize(attachments)),
|
||||||
|
attachments,
|
||||||
|
static_cast<u32>(ArraySize(subpass_descriptions)),
|
||||||
|
subpass_descriptions,
|
||||||
|
0,
|
||||||
|
nullptr};
|
||||||
|
|
||||||
|
VkResult res = vkCreateRenderPass(g_vulkan_context->GetDevice(), &pass_info, nullptr,
|
||||||
|
&m_encoding_render_pass);
|
||||||
|
if (res != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
LOG_VULKAN_ERROR(res, "vkCreateRenderPass (Encode) failed: ");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureConverter::CreateEncodingTexture()
|
||||||
|
{
|
||||||
|
m_encoding_render_texture = Texture2D::Create(
|
||||||
|
ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, ENCODING_TEXTURE_FORMAT,
|
||||||
|
VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
|
||||||
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
||||||
|
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
|
||||||
|
if (!m_encoding_render_texture)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
VkImageView framebuffer_attachments[] = {m_encoding_render_texture->GetView()};
|
||||||
|
VkFramebufferCreateInfo framebuffer_info = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
m_encoding_render_pass,
|
||||||
|
static_cast<u32>(ArraySize(framebuffer_attachments)),
|
||||||
|
framebuffer_attachments,
|
||||||
|
m_encoding_render_texture->GetWidth(),
|
||||||
|
m_encoding_render_texture->GetHeight(),
|
||||||
|
m_encoding_render_texture->GetLayers()};
|
||||||
|
|
||||||
|
VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr,
|
||||||
|
&m_encoding_render_framebuffer);
|
||||||
|
if (res != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: ");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureConverter::CreateEncodingDownloadTexture()
|
||||||
|
{
|
||||||
|
m_encoding_download_texture =
|
||||||
|
StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, ENCODING_TEXTURE_WIDTH,
|
||||||
|
ENCODING_TEXTURE_HEIGHT, ENCODING_TEXTURE_FORMAT);
|
||||||
|
|
||||||
|
return m_encoding_download_texture && m_encoding_download_texture->Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextureConverter::CompileYUYVConversionShaders()
|
||||||
|
{
|
||||||
|
static const char RGB_TO_YUYV_SHADER_SOURCE[] = R"(
|
||||||
|
SAMPLER_BINDING(0) uniform sampler2DArray source;
|
||||||
|
layout(location = 0) in vec3 uv0;
|
||||||
|
layout(location = 0) out vec4 ocol0;
|
||||||
|
|
||||||
|
const vec3 y_const = vec3(0.257,0.504,0.098);
|
||||||
|
const vec3 u_const = vec3(-0.148,-0.291,0.439);
|
||||||
|
const vec3 v_const = vec3(0.439,-0.368,-0.071);
|
||||||
|
const vec4 const3 = vec4(0.0625,0.5,0.0625,0.5);
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec3 c0 = texture(source, vec3(uv0.xy - dFdx(uv0.xy) * 0.25, 0.0)).rgb;
|
||||||
|
vec3 c1 = texture(source, vec3(uv0.xy + dFdx(uv0.xy) * 0.25, 0.0)).rgb;
|
||||||
|
vec3 c01 = (c0 + c1) * 0.5;
|
||||||
|
ocol0 = vec4(dot(c1, y_const),
|
||||||
|
dot(c01,u_const),
|
||||||
|
dot(c0,y_const),
|
||||||
|
dot(c01, v_const)) + const3;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
static const char YUYV_TO_RGB_SHADER_SOURCE[] = R"(
|
||||||
|
layout(std140, push_constant) uniform PCBlock
|
||||||
|
{
|
||||||
|
int buffer_offset;
|
||||||
|
int src_stride;
|
||||||
|
} PC;
|
||||||
|
|
||||||
|
TEXEL_BUFFER_BINDING(0) uniform samplerBuffer source;
|
||||||
|
layout(location = 0) in vec3 uv0;
|
||||||
|
layout(location = 0) out vec4 ocol0;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
ivec2 uv = ivec2(gl_FragCoord.xy);
|
||||||
|
int buffer_pos = PC.buffer_offset + uv.y * PC.src_stride + (uv.x / 2);
|
||||||
|
vec4 c0 = texelFetch(source, buffer_pos);
|
||||||
|
|
||||||
|
float y = mix(c0.r, c0.b, (uv.x & 1) == 1);
|
||||||
|
float yComp = 1.164 * (y - 0.0625);
|
||||||
|
float uComp = c0.g - 0.5;
|
||||||
|
float vComp = c0.a - 0.5;
|
||||||
|
ocol0 = vec4(yComp + (1.596 * vComp),
|
||||||
|
yComp - (0.813 * vComp) - (0.391 * uComp),
|
||||||
|
yComp + (2.018 * uComp),
|
||||||
|
1.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
std::string header = g_object_cache->GetUtilityShaderHeader();
|
||||||
|
std::string source = header + RGB_TO_YUYV_SHADER_SOURCE;
|
||||||
|
m_rgb_to_yuyv_shader = Util::CompileAndCreateFragmentShader(source);
|
||||||
|
source = header + YUYV_TO_RGB_SHADER_SOURCE;
|
||||||
|
m_yuyv_to_rgb_shader = Util::CompileAndCreateFragmentShader(source);
|
||||||
|
|
||||||
|
return m_rgb_to_yuyv_shader != VK_NULL_HANDLE && m_yuyv_to_rgb_shader != VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Vulkan
|
|
@ -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
|
|
@ -1,237 +0,0 @@
|
||||||
// Copyright 2016 Dolphin Emulator Project
|
|
||||||
// Licensed under GPLv2+
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#include "VideoBackends/Vulkan/TextureEncoder.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#include "Common/CommonFuncs.h"
|
|
||||||
#include "Common/Logging/Log.h"
|
|
||||||
#include "Common/MsgHandler.h"
|
|
||||||
|
|
||||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
|
||||||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
|
||||||
#include "VideoBackends/Vulkan/Renderer.h"
|
|
||||||
#include "VideoBackends/Vulkan/StagingTexture2D.h"
|
|
||||||
#include "VideoBackends/Vulkan/StateTracker.h"
|
|
||||||
#include "VideoBackends/Vulkan/Texture2D.h"
|
|
||||||
#include "VideoBackends/Vulkan/Util.h"
|
|
||||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
|
||||||
|
|
||||||
#include "VideoCommon/TextureConversionShader.h"
|
|
||||||
#include "VideoCommon/TextureDecoder.h"
|
|
||||||
|
|
||||||
namespace Vulkan
|
|
||||||
{
|
|
||||||
TextureEncoder::TextureEncoder()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureEncoder::~TextureEncoder()
|
|
||||||
{
|
|
||||||
if (m_encoding_render_pass != VK_NULL_HANDLE)
|
|
||||||
vkDestroyRenderPass(g_vulkan_context->GetDevice(), m_encoding_render_pass, nullptr);
|
|
||||||
|
|
||||||
if (m_encoding_texture_framebuffer != VK_NULL_HANDLE)
|
|
||||||
vkDestroyFramebuffer(g_vulkan_context->GetDevice(), m_encoding_texture_framebuffer, nullptr);
|
|
||||||
|
|
||||||
for (VkShaderModule shader : m_texture_encoding_shaders)
|
|
||||||
{
|
|
||||||
if (shader != VK_NULL_HANDLE)
|
|
||||||
vkDestroyShaderModule(g_vulkan_context->GetDevice(), shader, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureEncoder::Initialize()
|
|
||||||
{
|
|
||||||
if (!CompileShaders())
|
|
||||||
{
|
|
||||||
PanicAlert("Failed to compile shaders");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CreateEncodingRenderPass())
|
|
||||||
{
|
|
||||||
PanicAlert("Failed to create encode render pass");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CreateEncodingTexture())
|
|
||||||
{
|
|
||||||
PanicAlert("Failed to create encoding texture");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CreateDownloadTexture())
|
|
||||||
{
|
|
||||||
PanicAlert("Failed to create download texture");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextureEncoder::EncodeTextureToRam(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& src_rect)
|
|
||||||
{
|
|
||||||
if (m_texture_encoding_shaders[format] == VK_NULL_HANDLE)
|
|
||||||
{
|
|
||||||
ERROR_LOG(VIDEO, "Missing encoding fragment shader for format %u", format);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can't do our own draw within a render pass.
|
|
||||||
StateTracker::GetInstance()->EndRenderPass();
|
|
||||||
|
|
||||||
UtilityShaderDraw draw(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
|
||||||
g_object_cache->GetPushConstantPipelineLayout(), m_encoding_render_pass,
|
|
||||||
g_object_cache->GetScreenQuadVertexShader(), VK_NULL_HANDLE,
|
|
||||||
m_texture_encoding_shaders[format]);
|
|
||||||
|
|
||||||
// Uniform - int4 of left,top,native_width,scale
|
|
||||||
s32 position_uniform[4] = {src_rect.left, src_rect.top, static_cast<s32>(native_width),
|
|
||||||
scale_by_half ? 2 : 1};
|
|
||||||
draw.SetPushConstants(position_uniform, sizeof(position_uniform));
|
|
||||||
|
|
||||||
// Doesn't make sense to linear filter depth values
|
|
||||||
draw.SetPSSampler(0, src_texture, (scale_by_half && src_format != PEControl::Z24) ?
|
|
||||||
g_object_cache->GetLinearSampler() :
|
|
||||||
g_object_cache->GetPointSampler());
|
|
||||||
|
|
||||||
u32 render_width = bytes_per_row / sizeof(u32);
|
|
||||||
u32 render_height = num_blocks_y;
|
|
||||||
Util::SetViewportAndScissor(g_command_buffer_mgr->GetCurrentCommandBuffer(), 0, 0, render_width,
|
|
||||||
render_height);
|
|
||||||
|
|
||||||
// TODO: We could use compute shaders here.
|
|
||||||
VkRect2D render_region = {{0, 0}, {render_width, render_height}};
|
|
||||||
draw.BeginRenderPass(m_encoding_texture_framebuffer, render_region);
|
|
||||||
draw.DrawWithoutVertexBuffer(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 4);
|
|
||||||
draw.EndRenderPass();
|
|
||||||
|
|
||||||
// Transition the image before copying
|
|
||||||
m_encoding_texture->OverrideImageLayout(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
|
||||||
m_download_texture->CopyFromImage(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
|
||||||
m_encoding_texture->GetImage(), VK_IMAGE_ASPECT_COLOR_BIT, 0, 0,
|
|
||||||
render_width, render_height, 0, 0);
|
|
||||||
|
|
||||||
// Block until the GPU has finished copying to the staging texture.
|
|
||||||
Util::ExecuteCurrentCommandsAndRestoreState(false, true);
|
|
||||||
|
|
||||||
// Copy from staging texture to the final destination, adjusting pitch if necessary.
|
|
||||||
m_download_texture->ReadTexels(0, 0, render_width, render_height, dest_ptr, memory_stride);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureEncoder::CompileShaders()
|
|
||||||
{
|
|
||||||
// Texture encoding shaders
|
|
||||||
static const u32 texture_encoding_shader_formats[] = {
|
|
||||||
GX_TF_I4, GX_TF_I8, GX_TF_IA4, GX_TF_IA8, GX_TF_RGB565, GX_TF_RGB5A3, GX_TF_RGBA8,
|
|
||||||
GX_CTF_R4, GX_CTF_RA4, GX_CTF_RA8, GX_CTF_A8, GX_CTF_R8, GX_CTF_G8, GX_CTF_B8,
|
|
||||||
GX_CTF_RG8, GX_CTF_GB8, GX_CTF_Z8H, GX_TF_Z8, GX_CTF_Z16R, GX_TF_Z16, GX_TF_Z24X8,
|
|
||||||
GX_CTF_Z4, GX_CTF_Z8M, GX_CTF_Z8L, GX_CTF_Z16L};
|
|
||||||
for (u32 format : texture_encoding_shader_formats)
|
|
||||||
{
|
|
||||||
const char* shader_source =
|
|
||||||
TextureConversionShader::GenerateEncodingShader(format, APIType::Vulkan);
|
|
||||||
m_texture_encoding_shaders[format] = Util::CompileAndCreateFragmentShader(shader_source);
|
|
||||||
if (m_texture_encoding_shaders[format] == VK_NULL_HANDLE)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureEncoder::CreateEncodingRenderPass()
|
|
||||||
{
|
|
||||||
VkAttachmentDescription attachments[] = {
|
|
||||||
{0, ENCODING_TEXTURE_FORMAT, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
||||||
VK_ATTACHMENT_STORE_OP_STORE, VK_ATTACHMENT_LOAD_OP_DONT_CARE,
|
|
||||||
VK_ATTACHMENT_STORE_OP_DONT_CARE, VK_IMAGE_LAYOUT_UNDEFINED,
|
|
||||||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL}};
|
|
||||||
|
|
||||||
VkAttachmentReference color_attachment_references[] = {
|
|
||||||
{0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}};
|
|
||||||
|
|
||||||
VkSubpassDescription subpass_descriptions[] = {{0, VK_PIPELINE_BIND_POINT_GRAPHICS, 0, nullptr, 1,
|
|
||||||
color_attachment_references, nullptr, nullptr, 0,
|
|
||||||
nullptr}};
|
|
||||||
|
|
||||||
VkSubpassDependency dependancies[] = {
|
|
||||||
{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 pass_info = {VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
|
|
||||||
nullptr,
|
|
||||||
0,
|
|
||||||
static_cast<u32>(ArraySize(attachments)),
|
|
||||||
attachments,
|
|
||||||
static_cast<u32>(ArraySize(subpass_descriptions)),
|
|
||||||
subpass_descriptions,
|
|
||||||
static_cast<u32>(ArraySize(dependancies)),
|
|
||||||
dependancies};
|
|
||||||
|
|
||||||
VkResult res = vkCreateRenderPass(g_vulkan_context->GetDevice(), &pass_info, nullptr,
|
|
||||||
&m_encoding_render_pass);
|
|
||||||
if (res != VK_SUCCESS)
|
|
||||||
{
|
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateRenderPass (Encode) failed: ");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureEncoder::CreateEncodingTexture()
|
|
||||||
{
|
|
||||||
// From OGL: Why do we create a 1024 height texture?
|
|
||||||
m_encoding_texture = Texture2D::Create(
|
|
||||||
ENCODING_TEXTURE_WIDTH, ENCODING_TEXTURE_HEIGHT, 1, 1, ENCODING_TEXTURE_FORMAT,
|
|
||||||
VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
|
|
||||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT |
|
|
||||||
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
|
|
||||||
if (!m_encoding_texture)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
VkImageView framebuffer_attachments[] = {m_encoding_texture->GetView()};
|
|
||||||
VkFramebufferCreateInfo framebuffer_info = {VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
|
|
||||||
nullptr,
|
|
||||||
0,
|
|
||||||
m_encoding_render_pass,
|
|
||||||
static_cast<u32>(ArraySize(framebuffer_attachments)),
|
|
||||||
framebuffer_attachments,
|
|
||||||
m_encoding_texture->GetWidth(),
|
|
||||||
m_encoding_texture->GetHeight(),
|
|
||||||
m_encoding_texture->GetLayers()};
|
|
||||||
|
|
||||||
VkResult res = vkCreateFramebuffer(g_vulkan_context->GetDevice(), &framebuffer_info, nullptr,
|
|
||||||
&m_encoding_texture_framebuffer);
|
|
||||||
if (res != VK_SUCCESS)
|
|
||||||
{
|
|
||||||
LOG_VULKAN_ERROR(res, "vkCreateFramebuffer failed: ");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TextureEncoder::CreateDownloadTexture()
|
|
||||||
{
|
|
||||||
m_download_texture =
|
|
||||||
StagingTexture2D::Create(STAGING_BUFFER_TYPE_READBACK, ENCODING_TEXTURE_WIDTH,
|
|
||||||
ENCODING_TEXTURE_HEIGHT, ENCODING_TEXTURE_FORMAT);
|
|
||||||
|
|
||||||
if (!m_download_texture || !m_download_texture->Map())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Vulkan
|
|
|
@ -1,62 +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/VulkanLoader.h"
|
|
||||||
#include "VideoCommon/BPMemory.h"
|
|
||||||
#include "VideoCommon/VideoCommon.h"
|
|
||||||
|
|
||||||
namespace Vulkan
|
|
||||||
{
|
|
||||||
class StagingTexture2D;
|
|
||||||
class Texture2D;
|
|
||||||
|
|
||||||
class TextureEncoder
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TextureEncoder();
|
|
||||||
~TextureEncoder();
|
|
||||||
|
|
||||||
VkRenderPass GetEncodingRenderPass() const { return m_encoding_render_pass; }
|
|
||||||
Texture2D* GetEncodingTexture() const { return m_encoding_texture.get(); }
|
|
||||||
VkFramebuffer GetEncodingTextureFramebuffer() const { return m_encoding_texture_framebuffer; }
|
|
||||||
StagingTexture2D* GetDownloadTexture() const { return m_download_texture.get(); }
|
|
||||||
bool Initialize();
|
|
||||||
|
|
||||||
// Uses an encoding shader to copy src_texture to dest_ptr.
|
|
||||||
// Assumes that no render pass is currently in progress.
|
|
||||||
// WARNING: Executes the current command buffer.
|
|
||||||
void EncodeTextureToRam(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);
|
|
||||||
|
|
||||||
private:
|
|
||||||
// From OGL.
|
|
||||||
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;
|
|
||||||
|
|
||||||
bool CompileShaders();
|
|
||||||
bool CreateEncodingRenderPass();
|
|
||||||
bool CreateEncodingTexture();
|
|
||||||
bool CreateDownloadTexture();
|
|
||||||
|
|
||||||
std::array<VkShaderModule, NUM_TEXTURE_ENCODING_SHADERS> m_texture_encoding_shaders = {};
|
|
||||||
|
|
||||||
VkRenderPass m_encoding_render_pass = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
std::unique_ptr<Texture2D> m_encoding_texture;
|
|
||||||
VkFramebuffer m_encoding_texture_framebuffer = VK_NULL_HANDLE;
|
|
||||||
|
|
||||||
std::unique_ptr<StagingTexture2D> m_download_texture;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Vulkan
|
|
|
@ -364,6 +364,15 @@ void UtilityShaderDraw::SetPSSampler(size_t index, VkImageView view, VkSampler s
|
||||||
m_ps_samplers[index].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
m_ps_samplers[index].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UtilityShaderDraw::SetPSTexelBuffer(VkBufferView view)
|
||||||
|
{
|
||||||
|
// Should only be used with the texture conversion pipeline layout.
|
||||||
|
_assert_(m_pipeline_info.pipeline_layout ==
|
||||||
|
g_object_cache->GetPipelineLayout(PIPELINE_LAYOUT_TEXTURE_CONVERSION));
|
||||||
|
|
||||||
|
m_ps_texel_buffer = view;
|
||||||
|
}
|
||||||
|
|
||||||
void UtilityShaderDraw::SetRasterizationState(const RasterizationState& state)
|
void UtilityShaderDraw::SetRasterizationState(const RasterizationState& state)
|
||||||
{
|
{
|
||||||
m_pipeline_info.rasterization_state.bits = state.bits;
|
m_pipeline_info.rasterization_state.bits = state.bits;
|
||||||
|
@ -511,7 +520,7 @@ void UtilityShaderDraw::BindVertexBuffer()
|
||||||
void UtilityShaderDraw::BindDescriptors()
|
void UtilityShaderDraw::BindDescriptors()
|
||||||
{
|
{
|
||||||
// TODO: This method is a mess, clean it up
|
// TODO: This method is a mess, clean it up
|
||||||
std::array<VkDescriptorSet, NUM_DESCRIPTOR_SETS> bind_descriptor_sets = {};
|
std::array<VkDescriptorSet, NUM_DESCRIPTOR_SET_BIND_POINTS> bind_descriptor_sets = {};
|
||||||
std::array<VkWriteDescriptorSet, NUM_UBO_DESCRIPTOR_SET_BINDINGS + NUM_PIXEL_SHADER_SAMPLERS>
|
std::array<VkWriteDescriptorSet, NUM_UBO_DESCRIPTOR_SET_BINDINGS + NUM_PIXEL_SHADER_SAMPLERS>
|
||||||
set_writes = {};
|
set_writes = {};
|
||||||
uint32_t num_set_writes = 0;
|
uint32_t num_set_writes = 0;
|
||||||
|
@ -523,7 +532,7 @@ void UtilityShaderDraw::BindDescriptors()
|
||||||
if (m_vs_uniform_buffer.buffer != VK_NULL_HANDLE || m_ps_uniform_buffer.buffer != VK_NULL_HANDLE)
|
if (m_vs_uniform_buffer.buffer != VK_NULL_HANDLE || m_ps_uniform_buffer.buffer != VK_NULL_HANDLE)
|
||||||
{
|
{
|
||||||
VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(
|
VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||||
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_UNIFORM_BUFFERS));
|
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS));
|
||||||
if (set == VK_NULL_HANDLE)
|
if (set == VK_NULL_HANDLE)
|
||||||
PanicAlert("Failed to allocate descriptor set for utility draw");
|
PanicAlert("Failed to allocate descriptor set for utility draw");
|
||||||
|
|
||||||
|
@ -552,7 +561,7 @@ void UtilityShaderDraw::BindDescriptors()
|
||||||
&dummy_uniform_buffer,
|
&dummy_uniform_buffer,
|
||||||
nullptr};
|
nullptr};
|
||||||
|
|
||||||
bind_descriptor_sets[DESCRIPTOR_SET_UNIFORM_BUFFERS] = set;
|
bind_descriptor_sets[DESCRIPTOR_SET_LAYOUT_UNIFORM_BUFFERS] = set;
|
||||||
}
|
}
|
||||||
|
|
||||||
// PS samplers
|
// PS samplers
|
||||||
|
@ -572,7 +581,7 @@ void UtilityShaderDraw::BindDescriptors()
|
||||||
{
|
{
|
||||||
// Allocate a new descriptor set
|
// Allocate a new descriptor set
|
||||||
VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(
|
VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||||
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS));
|
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_PIXEL_SHADER_SAMPLERS));
|
||||||
if (set == VK_NULL_HANDLE)
|
if (set == VK_NULL_HANDLE)
|
||||||
PanicAlert("Failed to allocate descriptor set for utility draw");
|
PanicAlert("Failed to allocate descriptor set for utility draw");
|
||||||
|
|
||||||
|
@ -594,35 +603,65 @@ void UtilityShaderDraw::BindDescriptors()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bind_descriptor_sets[DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS] = set;
|
bind_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_PIXEL_SHADER_SAMPLERS] = set;
|
||||||
}
|
}
|
||||||
|
|
||||||
vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), num_set_writes, set_writes.data(), 0,
|
vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), num_set_writes, set_writes.data(), 0,
|
||||||
nullptr);
|
nullptr);
|
||||||
|
|
||||||
// Bind only the sets we updated
|
if (m_ps_texel_buffer != VK_NULL_HANDLE)
|
||||||
if (bind_descriptor_sets[0] != VK_NULL_HANDLE && bind_descriptor_sets[1] == VK_NULL_HANDLE)
|
|
||||||
{
|
{
|
||||||
// UBO only
|
// TODO: Handle case where this fails.
|
||||||
|
// This'll only be when we do over say, 1024 allocations per frame, which shouldn't happen.
|
||||||
|
// TODO: Execute the command buffer, reset render passes and then try again.
|
||||||
|
VkDescriptorSet set = g_command_buffer_mgr->AllocateDescriptorSet(
|
||||||
|
g_object_cache->GetDescriptorSetLayout(DESCRIPTOR_SET_LAYOUT_TEXEL_BUFFERS));
|
||||||
|
if (set == VK_NULL_HANDLE)
|
||||||
|
{
|
||||||
|
PanicAlert("Failed to allocate texel buffer descriptor set for utility draw");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkWriteDescriptorSet set_write = {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
|
nullptr,
|
||||||
|
set,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER,
|
||||||
|
nullptr,
|
||||||
|
nullptr,
|
||||||
|
&m_ps_texel_buffer};
|
||||||
|
vkUpdateDescriptorSets(g_vulkan_context->GetDevice(), 1, &set_write, 0, nullptr);
|
||||||
|
bind_descriptor_sets[DESCRIPTOR_SET_BIND_POINT_STORAGE_OR_TEXEL_BUFFER] = set;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fast path when there are no gaps in the set bindings
|
||||||
|
u32 bind_point_index;
|
||||||
|
for (bind_point_index = 0; bind_point_index < NUM_DESCRIPTOR_SET_BIND_POINTS; bind_point_index++)
|
||||||
|
{
|
||||||
|
if (bind_descriptor_sets[bind_point_index] == VK_NULL_HANDLE)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (bind_point_index > 0)
|
||||||
|
{
|
||||||
|
// Bind the contiguous sets, any others after any gaps will be handled below
|
||||||
vkCmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
vkCmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||||
m_pipeline_info.pipeline_layout, DESCRIPTOR_SET_UNIFORM_BUFFERS, 1,
|
m_pipeline_info.pipeline_layout, 0, bind_point_index,
|
||||||
&bind_descriptor_sets[0], NUM_UBO_DESCRIPTOR_SET_BINDINGS,
|
&bind_descriptor_sets[0], NUM_UBO_DESCRIPTOR_SET_BINDINGS,
|
||||||
m_ubo_offsets.data());
|
m_ubo_offsets.data());
|
||||||
}
|
}
|
||||||
else if (bind_descriptor_sets[0] == VK_NULL_HANDLE && bind_descriptor_sets[1] != VK_NULL_HANDLE)
|
|
||||||
|
// Handle any remaining sets
|
||||||
|
for (u32 i = bind_point_index; i < NUM_DESCRIPTOR_SET_BIND_POINTS; i++)
|
||||||
{
|
{
|
||||||
// Samplers only
|
if (bind_descriptor_sets[i] == VK_NULL_HANDLE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// No need to worry about dynamic offsets here, since #0 will always be bound above.
|
||||||
vkCmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
vkCmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||||
m_pipeline_info.pipeline_layout, DESCRIPTOR_SET_PIXEL_SHADER_SAMPLERS,
|
m_pipeline_info.pipeline_layout, i, 1, &bind_descriptor_sets[i], 0,
|
||||||
1, &bind_descriptor_sets[1], 0, nullptr);
|
nullptr);
|
||||||
}
|
|
||||||
else if (bind_descriptor_sets[0] != VK_NULL_HANDLE && bind_descriptor_sets[1] != VK_NULL_HANDLE)
|
|
||||||
{
|
|
||||||
// Both
|
|
||||||
vkCmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
||||||
m_pipeline_info.pipeline_layout, DESCRIPTOR_SET_UNIFORM_BUFFERS, 2,
|
|
||||||
bind_descriptor_sets.data(), NUM_UBO_DESCRIPTOR_SET_BINDINGS,
|
|
||||||
m_ubo_offsets.data());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,8 @@ public:
|
||||||
|
|
||||||
void SetPSSampler(size_t index, VkImageView view, VkSampler sampler);
|
void SetPSSampler(size_t index, VkImageView view, VkSampler sampler);
|
||||||
|
|
||||||
|
void SetPSTexelBuffer(VkBufferView view);
|
||||||
|
|
||||||
void SetRasterizationState(const RasterizationState& state);
|
void SetRasterizationState(const RasterizationState& state);
|
||||||
void SetDepthStencilState(const DepthStencilState& state);
|
void SetDepthStencilState(const DepthStencilState& state);
|
||||||
void SetBlendState(const BlendState& state);
|
void SetBlendState(const BlendState& state);
|
||||||
|
@ -182,6 +184,8 @@ private:
|
||||||
|
|
||||||
std::array<VkDescriptorImageInfo, NUM_PIXEL_SHADER_SAMPLERS> m_ps_samplers = {};
|
std::array<VkDescriptorImageInfo, NUM_PIXEL_SHADER_SAMPLERS> m_ps_samplers = {};
|
||||||
|
|
||||||
|
VkBufferView m_ps_texel_buffer = VK_NULL_HANDLE;
|
||||||
|
|
||||||
PipelineInfo m_pipeline_info = {};
|
PipelineInfo m_pipeline_info = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -53,12 +53,11 @@
|
||||||
<ClCompile Include="CommandBufferManager.cpp" />
|
<ClCompile Include="CommandBufferManager.cpp" />
|
||||||
<ClCompile Include="FramebufferManager.cpp" />
|
<ClCompile Include="FramebufferManager.cpp" />
|
||||||
<ClCompile Include="main.cpp" />
|
<ClCompile Include="main.cpp" />
|
||||||
<ClCompile Include="PaletteTextureConverter.cpp" />
|
<ClCompile Include="TextureConverter.cpp" />
|
||||||
<ClCompile Include="PerfQuery.cpp" />
|
<ClCompile Include="PerfQuery.cpp" />
|
||||||
<ClCompile Include="RasterFont.cpp" />
|
<ClCompile Include="RasterFont.cpp" />
|
||||||
<ClCompile Include="StagingBuffer.cpp" />
|
<ClCompile Include="StagingBuffer.cpp" />
|
||||||
<ClCompile Include="StagingTexture2D.cpp" />
|
<ClCompile Include="StagingTexture2D.cpp" />
|
||||||
<ClCompile Include="TextureEncoder.cpp" />
|
|
||||||
<ClCompile Include="Util.cpp" />
|
<ClCompile Include="Util.cpp" />
|
||||||
<ClCompile Include="VertexFormat.cpp" />
|
<ClCompile Include="VertexFormat.cpp" />
|
||||||
<ClCompile Include="ObjectCache.cpp" />
|
<ClCompile Include="ObjectCache.cpp" />
|
||||||
|
@ -78,11 +77,10 @@
|
||||||
<ClInclude Include="CommandBufferManager.h" />
|
<ClInclude Include="CommandBufferManager.h" />
|
||||||
<ClInclude Include="FramebufferManager.h" />
|
<ClInclude Include="FramebufferManager.h" />
|
||||||
<ClInclude Include="Constants.h" />
|
<ClInclude Include="Constants.h" />
|
||||||
<ClInclude Include="PaletteTextureConverter.h" />
|
<ClInclude Include="TextureConverter.h" />
|
||||||
<ClInclude Include="RasterFont.h" />
|
<ClInclude Include="RasterFont.h" />
|
||||||
<ClInclude Include="StagingBuffer.h" />
|
<ClInclude Include="StagingBuffer.h" />
|
||||||
<ClInclude Include="StagingTexture2D.h" />
|
<ClInclude Include="StagingTexture2D.h" />
|
||||||
<ClInclude Include="TextureEncoder.h" />
|
|
||||||
<ClInclude Include="Util.h" />
|
<ClInclude Include="Util.h" />
|
||||||
<ClInclude Include="VertexFormat.h" />
|
<ClInclude Include="VertexFormat.h" />
|
||||||
<ClInclude Include="PerfQuery.h" />
|
<ClInclude Include="PerfQuery.h" />
|
||||||
|
@ -116,4 +114,4 @@
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
</Project>
|
</Project>
|
Loading…
Reference in New Issue