Permalink
Browse files

Merge pull request #10096 from hrydgard/vulkan-postproc

Vulkan: Implement post-processing.
  • Loading branch information...
hrydgard committed Nov 12, 2017
2 parents 406ef92 + b8dc4d1 commit 190f363be1ba6acb79abb5fa31a5ceb6a3ac3207
View
@@ -1265,6 +1265,8 @@ set(GPU_SOURCES
GPU/Common/ShaderUniforms.h
GPU/Common/ShaderCommon.cpp
GPU/Common/ShaderCommon.h
GPU/Common/ShaderTranslation.cpp
GPU/Common/ShaderTranslation.h
GPU/Common/SplineCommon.cpp
GPU/Common/SplineCommon.h
GPU/Common/StencilCommon.cpp
@@ -69,7 +69,7 @@ class VulkanFBO;
struct PostShaderUniforms {
float texelDelta[2]; float pixelDelta[2];
float time[4];
bool video;
float video;
};
struct VirtualFramebuffer {
@@ -17,8 +17,6 @@
#include "ppsspp_config.h"
#if !defined(ANDROID)
#include <memory>
#include <vector>
#include <sstream>
@@ -34,13 +32,16 @@
#include "ShaderTranslation.h"
#include "ext/glslang/SPIRV/GlslangToSpv.h"
#include "thin3d/thin3d.h"
#if !defined(ANDROID)
#include "ext/SPIRV-Cross/spirv.hpp"
#include "ext/SPIRV-Cross/spirv_common.hpp"
#include "ext/SPIRV-Cross/spirv_cross.hpp"
#include "ext/SPIRV-Cross/spirv_glsl.hpp"
#ifdef _WIN32
#include "ext/SPIRV-Cross/spirv_hlsl.hpp"
#endif
#endif
extern void init_resources(TBuiltInResource &Resources);
@@ -58,12 +59,12 @@ static EShLanguage GetLanguage(const Draw::ShaderStage stage) {
void ShaderTranslationInit() {
// TODO: We have TLS issues on UWP
#if !PPSSPP_PLATFORM(UWP)
#if !PPSSPP_PLATFORM(UWP) && !defined(ANDROID)
glslang::InitializeProcess();
#endif
}
void ShaderTranslationShutdown() {
#if !PPSSPP_PLATFORM(UWP)
#if !PPSSPP_PLATFORM(UWP) && !defined(ANDROID)
glslang::FinalizeProcess();
#endif
}
@@ -83,7 +84,22 @@ cbuffer data : register(b0) {
float2 u_texelDelta;
float2 u_pixelDelta;
float4 u_time;
bool u_video;
float u_video;
};
)";
static const char *vulkanPrologue =
R"(#version 430
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
)";
static const char *pushconstantBufferDecl = R"(
layout(push_constant) uniform data {
vec2 u_texelDelta;
vec2 u_pixelDelta;
vec4 u_time;
float u_video;
};
)";
@@ -112,10 +128,78 @@ std::string Postprocess(std::string code, ShaderLanguage lang, Draw::ShaderStage
return output;
}
bool ConvertToVulkanGLSL(std::string *dest, TranslatedShaderMetadata *destMetadata, std::string src, Draw::ShaderStage stage, std::string *errorMessage) {
std::stringstream out;
static struct {
Draw::ShaderStage stage;
const char *needle;
const char *replacement;
} replacements[] = {
{ Draw::ShaderStage::VERTEX, "attribute vec4 a_position;", "layout(location = 0) in vec4 a_position;" },
{ Draw::ShaderStage::VERTEX, "attribute vec2 a_texcoord0;", "layout(location = 1) in vec2 a_texcoord0;"},
{ Draw::ShaderStage::VERTEX, "varying vec2 v_position;", "layout(location = 0) out vec2 v_position;" },
{ Draw::ShaderStage::FRAGMENT, "varying vec2 v_position;", "layout(location = 0) in vec2 v_position;" },
{ Draw::ShaderStage::FRAGMENT, "texture2D(", "texture(" },
{ Draw::ShaderStage::FRAGMENT, "gl_FragColor", "fragColor0" },
};
out << vulkanPrologue;
if (stage == Draw::ShaderStage::FRAGMENT) {
out << "layout (location = 0) out vec4 fragColor0;\n";
}
// Output the uniform buffer.
out << pushconstantBufferDecl;
// Alright, now let's go through it line by line and zap the single uniforms
// and perform replacements.
std::string line;
std::stringstream instream(src);
while (std::getline(instream, line)) {
char buffer[256];
int vecSize, num;
if (line.find("uniform bool") != std::string::npos) {
continue;
} else if (line.find("uniform sampler2D") == 0) {
line = "layout(set = 0, binding = 0) " + line;
} else if (line.find("uniform ") != std::string::npos) {
continue;
} else if (2 == sscanf(line.c_str(), "varying vec%d v_texcoord%d;", &vecSize, &num)) {
if (stage == Draw::ShaderStage::FRAGMENT) {
line = StringFromFormat("layout(location = %d) in vec%d v_texcoord%d;", num, vecSize, num);
} else {
line = StringFromFormat("layout(location = %d) out vec%d v_texcoord%d;", num, vecSize, num);
}
}
for (int i = 0; i < ARRAY_SIZE(replacements); i++) {
if (replacements[i].stage == stage)
line = ReplaceAll(line, replacements[i].needle, replacements[i].replacement);
}
out << line << "\n";
}
// DUMPLOG(src.c_str());
// ILOG("---->");
// DUMPLOG(LineNumberString(out.str()).c_str());
*dest = out.str();
return true;
}
bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShaderMetadata *destMetadata, std::string src, ShaderLanguage srcLang, Draw::ShaderStage stage, std::string *errorMessage) {
if (srcLang != GLSL_300 && srcLang != GLSL_140)
return false;
if ((srcLang == GLSL_140 || srcLang == GLSL_300) && destLang == GLSL_VULKAN) {
// Let's just mess about at the string level, no need to recompile.
bool result = ConvertToVulkanGLSL(dest, destMetadata, src, stage, errorMessage);
return result;
}
#if defined(ANDROID)
return false;
#else
#if PPSSPP_PLATFORM(UWP)
return false;
#endif
@@ -126,8 +210,8 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade
TBuiltInResource Resources;
init_resources(Resources);
// Enable SPIR-V and Vulkan rules when parsing GLSL
EShMessages messages = EShMessages::EShMsgDefault; // (EShMessages)(EShMsgSpvRules | EShMsgVulkanRules);
// Don't enable SPIR-V and Vulkan rules when parsing GLSL. Our postshaders are written in oldschool GLES 2.0.
EShMessages messages = EShMessages::EShMsgDefault;
EShLanguage shaderStage = GetLanguage(stage);
glslang::TShader shader(shaderStage);
@@ -166,8 +250,6 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade
// Alright, step 1 done. Now let's take this SPIR-V shader and output in our desired format.
switch (destLang) {
case GLSL_VULKAN:
return false; // TODO
#ifdef _WIN32
case HLSL_DX9:
{
@@ -227,6 +309,5 @@ bool TranslateShader(std::string *dest, ShaderLanguage destLang, TranslatedShade
default:
return false;
}
}
#endif
}
@@ -21,8 +21,6 @@
#include "GPU/Common/ShaderCommon.h"
#include "thin3d/thin3d.h"
#ifndef ANDROID
struct TranslatedShaderMetadata {
};
@@ -31,5 +29,3 @@ void ShaderTranslationInit();
void ShaderTranslationShutdown();
bool TranslateShader(std::string *dst, ShaderLanguage destLang, TranslatedShaderMetadata *destMetadata, std::string src, ShaderLanguage srcLang, Draw::ShaderStage stage, std::string *errorMessage);
#endif
@@ -23,6 +23,8 @@
#include "base/timeutil.h"
#include "math/lin/matrix4x4.h"
#include "math/dataconv.h"
#include "i18n/i18n.h"
#include "ext/native/file/vfs.h"
#include "ext/native/thin3d/thin3d.h"
#include "Common/Vulkan/VulkanContext.h"
@@ -39,6 +41,7 @@
#include "GPU/ge_constants.h"
#include "GPU/GPUState.h"
#include "GPU/Common/ShaderTranslation.h"
#include "GPU/Common/PostShader.h"
#include "GPU/Common/TextureDecoder.h"
#include "GPU/Common/FramebufferCommon.h"
@@ -155,6 +158,12 @@ void FramebufferManagerVulkan::DestroyDeviceObjects() {
vulkan_->Delete().QueueDeleteSampler(linearSampler_);
if (nearestSampler_ != VK_NULL_HANDLE)
vulkan_->Delete().QueueDeleteSampler(nearestSampler_);
if (postVs_)
vulkan_->Delete().QueueDeleteShaderModule(postVs_);
if (postFs_)
vulkan_->Delete().QueueDeleteShaderModule(postFs_);
pipelinePostShader_ = VK_NULL_HANDLE; // actual pipeline should get destroyed by vulkan2d.
}
void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, bool clearDepth, uint32_t color, float depth) {
@@ -180,28 +189,6 @@ void FramebufferManagerVulkan::NotifyClear(bool clearColor, bool clearAlpha, boo
}
}
void FramebufferManagerVulkan::UpdatePostShaderUniforms(int bufferWidth, int bufferHeight, int renderWidth, int renderHeight) {
float u_delta = 1.0f / renderWidth;
float v_delta = 1.0f / renderHeight;
float u_pixel_delta = u_delta;
float v_pixel_delta = v_delta;
if (postShaderAtOutputResolution_) {
float x, y, w, h;
CenterDisplayOutputRect(&x, &y, &w, &h, 480.0f, 272.0f, (float)pixelWidth_, (float)pixelHeight_, ROTATION_LOCKED_HORIZONTAL);
u_pixel_delta = (1.0f / w) * (480.0f / bufferWidth);
v_pixel_delta = (1.0f / h) * (272.0f / bufferHeight);
}
postUniforms_.texelDelta[0] = u_delta;
postUniforms_.texelDelta[1] = v_delta;
postUniforms_.pixelDelta[0] = u_pixel_delta;
postUniforms_.pixelDelta[1] = v_pixel_delta;
int flipCount = __DisplayGetFlipCount();
int vCount = __DisplayGetVCount();
float time[4] = { time_now(), (vCount % 60) * 1.0f / 60.0f, (float)vCount, (float)(flipCount % 60) };
memcpy(postUniforms_.time, time, 4 * sizeof(float));
}
void FramebufferManagerVulkan::Init() {
FramebufferManagerCommon::Init();
// Workaround for upscaling shaders where we force x1 resolution without saving it
@@ -349,6 +336,9 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa
VkBuffer vbuffer;
VkDeviceSize offset = push_->Push(vtx, sizeof(vtx), &vbuffer);
renderManager->BindPipeline(cur2DPipeline_);
if (cur2DPipeline_ == pipelinePostShader_) {
renderManager->PushConstants(vulkan2D_->GetPipelineLayout(), VK_SHADER_STAGE_FRAGMENT_BIT | VK_SHADER_STAGE_VERTEX_BIT, 0, (int)sizeof(postShaderUniforms_), &postShaderUniforms_);
}
renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, vbuffer, offset, 4);
}
@@ -358,7 +348,23 @@ void FramebufferManagerVulkan::Bind2DShader() {
}
void FramebufferManagerVulkan::BindPostShader(const PostShaderUniforms &uniforms) {
Bind2DShader();
if (!pipelinePostShader_) {
if (usePostShader_) {
CompilePostShader();
}
if (!usePostShader_) {
SetNumExtraFBOs(0);
Bind2DShader();
return;
} else {
SetNumExtraFBOs(1);
}
}
postShaderUniforms_ = uniforms;
// VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
cur2DPipeline_ = pipelinePostShader_;
gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE);
}
@@ -626,4 +632,72 @@ void FramebufferManagerVulkan::Resized() {
if (UpdateSize()) {
DestroyAllFBOs();
}
// Might have a new post shader - let's compile it.
CompilePostShader();
}
void FramebufferManagerVulkan::CompilePostShader() {
if (postVs_) {
vulkan_->Delete().QueueDeleteShaderModule(postVs_);
}
if (postFs_) {
vulkan_->Delete().QueueDeleteShaderModule(postFs_);
}
const ShaderInfo *shaderInfo = nullptr;
if (g_Config.sPostShaderName == "Off") {
usePostShader_ = false;
return;
}
usePostShader_ = false;
ReloadAllPostShaderInfo();
shaderInfo = GetPostShaderInfo(g_Config.sPostShaderName);
std::string errorVSX, errorFSX;
std::string vsSource;
std::string fsSource;
if (shaderInfo) {
postShaderAtOutputResolution_ = shaderInfo->outputResolution;
size_t sz;
char *vs = (char *)VFSReadFile(shaderInfo->vertexShaderFile.c_str(), &sz);
if (!vs)
return;
char *fs = (char *)VFSReadFile(shaderInfo->fragmentShaderFile.c_str(), &sz);
if (!fs) {
free(vs);
return;
}
std::string vsSourceGLSL = vs;
std::string fsSourceGLSL = fs;
free(vs);
free(fs);
TranslatedShaderMetadata metaVS, metaFS;
if (!TranslateShader(&vsSource, GLSL_VULKAN, &metaVS, vsSourceGLSL, GLSL_140, Draw::ShaderStage::VERTEX, &errorVSX))
return;
if (!TranslateShader(&fsSource, GLSL_VULKAN, &metaFS, fsSourceGLSL, GLSL_140, Draw::ShaderStage::FRAGMENT, &errorFSX))
return;
} else {
return;
}
I18NCategory *gr = GetI18NCategory("Graphics");
// TODO: Delete the old pipeline?
std::string errorVS;
std::string errorFS;
postVs_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_VERTEX_BIT, vsSource.c_str(), &errorVS);
postFs_ = CompileShaderModule(vulkan_, VK_SHADER_STAGE_FRAGMENT_BIT, fsSource.c_str(), &errorFS);
VkRenderPass backbufferRP = (VkRenderPass)draw_->GetNativeObject(Draw::NativeObject::BACKBUFFER_RENDERPASS);
if (postVs_ && postFs_) {
pipelinePostShader_ = vulkan2D_->GetPipeline(backbufferRP, postVs_, postFs_, true, Vulkan2D::VK2DDepthStencilMode::NONE);
} else {
ELOG("Failed to compile.");
}
usePostShader_ = true;
}
Oops, something went wrong.

0 comments on commit 190f363

Please sign in to comment.