summaryrefslogtreecommitdiff
path: root/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp
diff options
context:
space:
mode:
authorMat M <mathew1800@gmail.com>2016-09-30 19:15:50 -0400
committerGitHub <noreply@github.com>2016-09-30 19:15:50 -0400
commit46b9383280a623753686ab6df6c479e784e416ae (patch)
tree9ffb2cbdba6d39a29d6fed9df4db03166755a20d /Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp
parentf3cdc6433cba253fa4485cd7e27025c31410ed0c (diff)
parenta8194cff3ca8f9b85a779da6b8214b58ba3fa7d0 (diff)
Merge pull request #3935 from stenzek/vulkan-pr
Vulkan Backend
Diffstat (limited to 'Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp')
-rw-r--r--Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp238
1 files changed, 238 insertions, 0 deletions
diff --git a/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp b/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp
new file mode 100644
index 0000000000..b89baba62f
--- /dev/null
+++ b/Source/Core/VideoBackends/Vulkan/TextureEncoder.cpp
@@ -0,0 +1,238 @@
+// Copyright 2016 Dolphin Emulator Project
+// Licensed under GPLv2+
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cstring>
+
+#include "Common/CommonFuncs.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/TextureCache.h"
+#include "VideoBackends/Vulkan/TextureEncoder.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(StateTracker* state_tracker, 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.
+ state_tracker->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.
+ g_command_buffer_mgr->ExecuteCommandBuffer(false, true);
+ state_tracker->InvalidateDescriptorSets();
+ state_tracker->SetPendingRebind();
+
+ // 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_TRANSFER_SRC_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