2019-03-05 03:59:17 +00:00
|
|
|
|
|
|
|
#include "vk_postprocess.h"
|
|
|
|
#include "vk_renderbuffers.h"
|
|
|
|
#include "vulkan/shaders/vk_shader.h"
|
|
|
|
#include "vulkan/system/vk_builders.h"
|
|
|
|
#include "vulkan/system/vk_framebuffer.h"
|
|
|
|
#include "vulkan/system/vk_buffers.h"
|
2019-03-06 21:59:21 +00:00
|
|
|
#include "vulkan/system/vk_swapchain.h"
|
2019-03-07 17:05:12 +00:00
|
|
|
#include "vulkan/renderer/vk_renderstate.h"
|
2019-03-05 03:59:17 +00:00
|
|
|
#include "hwrenderer/utility/hw_cvars.h"
|
|
|
|
#include "hwrenderer/postprocessing/hw_presentshader.h"
|
|
|
|
#include "hwrenderer/postprocessing/hw_postprocess.h"
|
|
|
|
#include "hwrenderer/postprocessing/hw_postprocess_cvars.h"
|
|
|
|
#include "hwrenderer/utility/hw_vrmodes.h"
|
|
|
|
#include "hwrenderer/data/flatvertices.h"
|
|
|
|
#include "r_videoscale.h"
|
2019-03-05 18:49:06 +00:00
|
|
|
#include "w_wad.h"
|
2019-03-05 03:59:17 +00:00
|
|
|
|
2019-03-06 21:59:21 +00:00
|
|
|
EXTERN_CVAR(Int, gl_dither_bpc)
|
|
|
|
|
2019-03-05 03:59:17 +00:00
|
|
|
VkPostprocess::VkPostprocess()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
VkPostprocess::~VkPostprocess()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-03-07 17:05:12 +00:00
|
|
|
void VkPostprocess::SetActiveRenderTarget()
|
|
|
|
{
|
|
|
|
auto fb = GetVulkanFrameBuffer();
|
|
|
|
auto buffers = fb->GetBuffers();
|
|
|
|
fb->GetRenderPassManager()->SetRenderTarget(buffers->PipelineView[mCurrentPipelineImage].get(), buffers->GetWidth(), buffers->GetHeight());
|
|
|
|
}
|
|
|
|
|
2019-03-05 03:59:17 +00:00
|
|
|
void VkPostprocess::PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D)
|
|
|
|
{
|
|
|
|
auto fb = GetVulkanFrameBuffer();
|
|
|
|
|
|
|
|
hw_postprocess.fixedcm = fixedcm;
|
|
|
|
hw_postprocess.SceneWidth = fb->GetBuffers()->GetSceneWidth();
|
|
|
|
hw_postprocess.SceneHeight = fb->GetBuffers()->GetSceneHeight();
|
|
|
|
|
|
|
|
hw_postprocess.DeclareShaders();
|
|
|
|
hw_postprocess.UpdateTextures();
|
|
|
|
hw_postprocess.UpdateSteps();
|
|
|
|
|
|
|
|
CompileEffectShaders();
|
|
|
|
UpdateEffectTextures();
|
|
|
|
|
|
|
|
RenderEffect("UpdateCameraExposure");
|
|
|
|
//mCustomPostProcessShaders->Run("beforebloom");
|
|
|
|
RenderEffect("BloomScene");
|
2019-03-07 17:05:12 +00:00
|
|
|
SetActiveRenderTarget();
|
2019-03-05 03:59:17 +00:00
|
|
|
afterBloomDrawEndScene2D();
|
|
|
|
RenderEffect("TonemapScene");
|
|
|
|
RenderEffect("ColormapScene");
|
|
|
|
RenderEffect("LensDistortScene");
|
|
|
|
RenderEffect("ApplyFXAA");
|
|
|
|
//mCustomPostProcessShaders->Run("scene");
|
|
|
|
}
|
|
|
|
|
2019-03-07 17:05:12 +00:00
|
|
|
void VkPostprocess::BlitSceneToTexture()
|
|
|
|
{
|
|
|
|
auto fb = GetVulkanFrameBuffer();
|
|
|
|
|
|
|
|
fb->GetRenderState()->EndRenderPass();
|
|
|
|
|
|
|
|
auto buffers = fb->GetBuffers();
|
|
|
|
auto cmdbuffer = fb->GetDrawCommands();
|
|
|
|
|
|
|
|
auto sceneColor = buffers->SceneColor.get();
|
|
|
|
|
|
|
|
mCurrentPipelineImage = 0;
|
|
|
|
|
|
|
|
PipelineBarrier barrier0;
|
|
|
|
barrier0.addImage(sceneColor, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
|
|
|
|
barrier0.addImage(buffers->PipelineImage[mCurrentPipelineImage].get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
|
|
|
|
barrier0.execute(cmdbuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
|
|
|
|
2019-03-08 01:24:54 +00:00
|
|
|
if (buffers->GetSceneSamples() != VK_SAMPLE_COUNT_1_BIT)
|
2019-03-07 17:05:12 +00:00
|
|
|
{
|
|
|
|
VkImageResolve resolve = {};
|
|
|
|
resolve.srcOffset = { 0, 0, 0 };
|
|
|
|
resolve.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
resolve.srcSubresource.mipLevel = 0;
|
|
|
|
resolve.srcSubresource.baseArrayLayer = 0;
|
|
|
|
resolve.srcSubresource.layerCount = 1;
|
|
|
|
resolve.dstOffset = { 0, 0, 0 };
|
|
|
|
resolve.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
resolve.dstSubresource.mipLevel = 0;
|
|
|
|
resolve.dstSubresource.baseArrayLayer = 0;
|
|
|
|
resolve.dstSubresource.layerCount = 1;
|
|
|
|
resolve.extent = { (uint32_t)sceneColor->width, (uint32_t)sceneColor->height, 1 };
|
|
|
|
cmdbuffer->resolveImage(
|
|
|
|
sceneColor->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
|
|
buffers->PipelineImage[mCurrentPipelineImage]->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
|
|
1, &resolve);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VkImageBlit blit = {};
|
|
|
|
blit.srcOffsets[0] = { 0, 0, 0 };
|
|
|
|
blit.srcOffsets[1] = { sceneColor->width, sceneColor->height, 1 };
|
|
|
|
blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
blit.srcSubresource.mipLevel = 0;
|
|
|
|
blit.srcSubresource.baseArrayLayer = 0;
|
|
|
|
blit.srcSubresource.layerCount = 1;
|
|
|
|
blit.dstOffsets[0] = { 0, 0, 0 };
|
|
|
|
blit.dstOffsets[1] = { sceneColor->width, sceneColor->height, 1 };
|
|
|
|
blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
blit.dstSubresource.mipLevel = 0;
|
|
|
|
blit.dstSubresource.baseArrayLayer = 0;
|
|
|
|
blit.dstSubresource.layerCount = 1;
|
|
|
|
cmdbuffer->blitImage(
|
|
|
|
sceneColor->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
|
|
|
buffers->PipelineImage[mCurrentPipelineImage]->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
|
|
|
1, &blit, VK_FILTER_NEAREST);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: this destroys the sceneColor contents
|
|
|
|
PipelineBarrier barrier1;
|
|
|
|
barrier1.addImage(sceneColor, VK_IMAGE_LAYOUT_UNDEFINED/*VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL*/, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ACCESS_TRANSFER_READ_BIT, 0);
|
|
|
|
barrier1.execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
|
|
|
|
|
|
|
|
buffers->SceneColorLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
buffers->PipelineLayout[mCurrentPipelineImage] = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
|
|
|
}
|
|
|
|
|
2019-03-06 21:59:21 +00:00
|
|
|
void VkPostprocess::DrawPresentTexture(const IntRect &box, bool applyGamma, bool clearBorders)
|
|
|
|
{
|
|
|
|
auto fb = GetVulkanFrameBuffer();
|
|
|
|
|
|
|
|
hw_postprocess.DeclareShaders();
|
|
|
|
hw_postprocess.UpdateTextures();
|
|
|
|
hw_postprocess.UpdateSteps();
|
|
|
|
CompileEffectShaders();
|
|
|
|
UpdateEffectTextures();
|
|
|
|
|
|
|
|
PresentUniforms uniforms;
|
|
|
|
if (!applyGamma /*|| framebuffer->IsHWGammaActive()*/)
|
|
|
|
{
|
|
|
|
uniforms.InvGamma = 1.0f;
|
|
|
|
uniforms.Contrast = 1.0f;
|
|
|
|
uniforms.Brightness = 0.0f;
|
|
|
|
uniforms.Saturation = 1.0f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
uniforms.InvGamma = 1.0f / clamp<float>(Gamma, 0.1f, 4.f);
|
|
|
|
uniforms.Contrast = clamp<float>(vid_contrast, 0.1f, 3.f);
|
|
|
|
uniforms.Brightness = clamp<float>(vid_brightness, -0.8f, 0.8f);
|
|
|
|
uniforms.Saturation = clamp<float>(vid_saturation, -15.0f, 15.f);
|
|
|
|
uniforms.GrayFormula = static_cast<int>(gl_satformula);
|
|
|
|
}
|
|
|
|
uniforms.ColorScale = (gl_dither_bpc == -1) ? 255.0f : (float)((1 << gl_dither_bpc) - 1);
|
|
|
|
uniforms.Scale = { screen->mScreenViewport.width / (float)fb->GetBuffers()->GetWidth(), screen->mScreenViewport.height / (float)fb->GetBuffers()->GetHeight() };
|
|
|
|
|
|
|
|
PPStep step;
|
|
|
|
step.ShaderName = "Present";
|
|
|
|
step.Uniforms.Set(uniforms);
|
|
|
|
step.Viewport = box;
|
2019-03-07 17:05:12 +00:00
|
|
|
step.SetInputCurrent(0, ViewportLinearScale() ? PPFilterMode::Linear : PPFilterMode::Nearest);
|
2019-03-06 21:59:21 +00:00
|
|
|
step.SetInputTexture(1, "PresentDither", PPFilterMode::Nearest, PPWrapMode::Repeat);
|
|
|
|
step.SetOutputSwapChain();
|
|
|
|
step.SetNoBlend();
|
|
|
|
//if (clearBorders) step.SetClearBorders();
|
|
|
|
|
|
|
|
TArray<PPStep> steps;
|
|
|
|
steps.Push(step);
|
|
|
|
hw_postprocess.Effects["Present"] = steps;
|
|
|
|
|
|
|
|
RenderEffect("Present");
|
|
|
|
}
|
|
|
|
|
2019-03-05 03:59:17 +00:00
|
|
|
void VkPostprocess::AmbientOccludeScene(float m5)
|
|
|
|
{
|
|
|
|
auto fb = GetVulkanFrameBuffer();
|
|
|
|
|
|
|
|
hw_postprocess.SceneWidth = fb->GetBuffers()->GetSceneWidth();
|
|
|
|
hw_postprocess.SceneHeight = fb->GetBuffers()->GetSceneHeight();
|
|
|
|
hw_postprocess.m5 = m5;
|
|
|
|
|
|
|
|
hw_postprocess.DeclareShaders();
|
|
|
|
hw_postprocess.UpdateTextures();
|
|
|
|
hw_postprocess.UpdateSteps();
|
|
|
|
|
|
|
|
CompileEffectShaders();
|
|
|
|
UpdateEffectTextures();
|
|
|
|
|
|
|
|
RenderEffect("AmbientOccludeScene");
|
|
|
|
}
|
|
|
|
|
|
|
|
void VkPostprocess::BlurScene(float gameinfobluramount)
|
|
|
|
{
|
2019-03-08 01:24:54 +00:00
|
|
|
auto fb = GetVulkanFrameBuffer();
|
|
|
|
hw_postprocess.SceneWidth = fb->GetBuffers()->GetSceneWidth();
|
|
|
|
hw_postprocess.SceneHeight = fb->GetBuffers()->GetSceneHeight();
|
2019-03-05 03:59:17 +00:00
|
|
|
hw_postprocess.gameinfobluramount = gameinfobluramount;
|
|
|
|
|
|
|
|
hw_postprocess.DeclareShaders();
|
|
|
|
hw_postprocess.UpdateTextures();
|
|
|
|
hw_postprocess.UpdateSteps();
|
|
|
|
|
|
|
|
CompileEffectShaders();
|
|
|
|
UpdateEffectTextures();
|
|
|
|
|
|
|
|
auto vrmode = VRMode::GetVRMode(true);
|
|
|
|
int eyeCount = vrmode->mEyeCount;
|
|
|
|
for (int i = 0; i < eyeCount; ++i)
|
|
|
|
{
|
|
|
|
RenderEffect("BlurScene");
|
|
|
|
if (eyeCount - i > 1) NextEye(eyeCount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VkPostprocess::ClearTonemapPalette()
|
|
|
|
{
|
|
|
|
hw_postprocess.Textures.Remove("Tonemap.Palette");
|
|
|
|
}
|
|
|
|
|
2019-03-06 18:42:02 +00:00
|
|
|
void VkPostprocess::BeginFrame()
|
|
|
|
{
|
|
|
|
mFrameDescriptorSets.clear();
|
|
|
|
|
|
|
|
if (!mDescriptorPool)
|
|
|
|
{
|
|
|
|
DescriptorPoolBuilder builder;
|
2019-03-08 01:24:54 +00:00
|
|
|
builder.addPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 100);
|
|
|
|
builder.setMaxSets(100);
|
2019-03-06 18:42:02 +00:00
|
|
|
mDescriptorPool = builder.create(GetVulkanFrameBuffer()->device);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-05 03:59:17 +00:00
|
|
|
void VkPostprocess::RenderBuffersReset()
|
|
|
|
{
|
2019-03-05 22:31:38 +00:00
|
|
|
mRenderPassSetup.clear();
|
2019-03-05 03:59:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VkPostprocess::UpdateEffectTextures()
|
|
|
|
{
|
2019-03-05 19:39:27 +00:00
|
|
|
auto fb = GetVulkanFrameBuffer();
|
|
|
|
|
|
|
|
TMap<FString, PPTextureDesc>::Iterator it(hw_postprocess.Textures);
|
|
|
|
TMap<FString, PPTextureDesc>::Pair *pair;
|
|
|
|
while (it.NextPair(pair))
|
|
|
|
{
|
|
|
|
const auto &desc = pair->Value;
|
|
|
|
auto &vktex = mTextures[pair->Key];
|
|
|
|
|
|
|
|
if (vktex && (vktex->Image->width != desc.Width || vktex->Image->height != desc.Height))
|
|
|
|
vktex.reset();
|
|
|
|
|
|
|
|
if (!vktex)
|
|
|
|
{
|
|
|
|
vktex.reset(new VkPPTexture());
|
|
|
|
|
|
|
|
VkFormat format;
|
|
|
|
int pixelsize;
|
|
|
|
switch (pair->Value.Format)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case PixelFormat::Rgba8: format = VK_FORMAT_R8G8B8A8_UNORM; pixelsize = 4; break;
|
|
|
|
case PixelFormat::Rgba16f: format = VK_FORMAT_R16G16B16A16_SFLOAT; pixelsize = 8; break;
|
|
|
|
case PixelFormat::R32f: format = VK_FORMAT_R32_SFLOAT; pixelsize = 4; break;
|
|
|
|
case PixelFormat::Rg16f: format = VK_FORMAT_R16G16_SFLOAT; pixelsize = 4; break;
|
|
|
|
case PixelFormat::Rgba16_snorm: format = VK_FORMAT_R16G16B16A16_SNORM; pixelsize = 8; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ImageBuilder imgbuilder;
|
|
|
|
imgbuilder.setFormat(format);
|
|
|
|
imgbuilder.setSize(desc.Width, desc.Height);
|
|
|
|
if (desc.Data)
|
|
|
|
imgbuilder.setUsage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
|
|
|
|
else
|
|
|
|
imgbuilder.setUsage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
|
|
|
|
if (!imgbuilder.isFormatSupported(fb->device))
|
|
|
|
I_FatalError("Vulkan device does not support the image format required by %s\n", pair->Key.GetChars());
|
|
|
|
vktex->Image = imgbuilder.create(fb->device);
|
2019-03-05 22:31:38 +00:00
|
|
|
vktex->Format = format;
|
2019-03-05 19:39:27 +00:00
|
|
|
|
|
|
|
ImageViewBuilder viewbuilder;
|
|
|
|
viewbuilder.setImage(vktex->Image.get(), format);
|
|
|
|
vktex->View = viewbuilder.create(fb->device);
|
|
|
|
|
|
|
|
if (desc.Data)
|
|
|
|
{
|
|
|
|
size_t totalsize = desc.Width * desc.Height * pixelsize;
|
|
|
|
BufferBuilder stagingbuilder;
|
|
|
|
stagingbuilder.setSize(totalsize);
|
|
|
|
stagingbuilder.setUsage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY);
|
|
|
|
vktex->Staging = stagingbuilder.create(fb->device);
|
|
|
|
|
|
|
|
PipelineBarrier barrier0;
|
|
|
|
barrier0.addImage(vktex->Image.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT);
|
|
|
|
barrier0.execute(fb->GetUploadCommands(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
|
|
|
|
|
|
|
void *data = vktex->Staging->Map(0, totalsize);
|
|
|
|
memcpy(data, desc.Data.get(), totalsize);
|
|
|
|
vktex->Staging->Unmap();
|
|
|
|
|
|
|
|
VkBufferImageCopy region = {};
|
|
|
|
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
|
|
region.imageSubresource.layerCount = 1;
|
|
|
|
region.imageExtent.depth = 1;
|
|
|
|
region.imageExtent.width = desc.Width;
|
|
|
|
region.imageExtent.height = desc.Height;
|
|
|
|
fb->GetUploadCommands()->copyBufferToImage(vktex->Staging->buffer, vktex->Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
|
|
|
|
|
|
|
PipelineBarrier barrier1;
|
|
|
|
barrier1.addImage(vktex->Image.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
|
|
|
|
barrier1.execute(fb->GetUploadCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
|
|
|
vktex->Layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PipelineBarrier barrier;
|
|
|
|
barrier.addImage(vktex->Image.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT);
|
|
|
|
barrier.execute(fb->GetUploadCommands(), VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
|
|
|
|
vktex->Layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-03-05 03:59:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VkPostprocess::CompileEffectShaders()
|
|
|
|
{
|
2019-03-05 18:49:06 +00:00
|
|
|
auto fb = GetVulkanFrameBuffer();
|
|
|
|
|
|
|
|
TMap<FString, PPShader>::Iterator it(hw_postprocess.Shaders);
|
|
|
|
TMap<FString, PPShader>::Pair *pair;
|
|
|
|
while (it.NextPair(pair))
|
|
|
|
{
|
|
|
|
const auto &desc = pair->Value;
|
|
|
|
auto &vkshader = mShaders[pair->Key];
|
|
|
|
if (!vkshader)
|
|
|
|
{
|
2019-03-05 19:39:27 +00:00
|
|
|
vkshader.reset(new VkPPShader());
|
|
|
|
|
2019-03-05 18:49:06 +00:00
|
|
|
FString prolog;
|
|
|
|
if (!desc.Uniforms.empty())
|
2019-03-05 22:31:38 +00:00
|
|
|
prolog = UniformBlockDecl::Create("Uniforms", desc.Uniforms, -1);
|
2019-03-05 18:49:06 +00:00
|
|
|
prolog += desc.Defines;
|
|
|
|
|
|
|
|
ShaderBuilder vertbuilder;
|
|
|
|
vertbuilder.setVertexShader(LoadShaderCode(desc.VertexShader, "", desc.Version));
|
|
|
|
vkshader->VertexShader = vertbuilder.create(fb->device);
|
|
|
|
|
|
|
|
ShaderBuilder fragbuilder;
|
|
|
|
fragbuilder.setFragmentShader(LoadShaderCode(desc.FragmentShader, prolog, desc.Version));
|
|
|
|
vkshader->FragmentShader = fragbuilder.create(fb->device);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FString VkPostprocess::LoadShaderCode(const FString &lumpName, const FString &defines, int version)
|
|
|
|
{
|
|
|
|
int lump = Wads.CheckNumForFullName(lumpName, 0);
|
2019-03-06 09:39:39 +00:00
|
|
|
if (lump == -1) I_FatalError("Unable to load '%s'", lumpName.GetChars());
|
2019-03-05 18:49:06 +00:00
|
|
|
FString code = Wads.ReadLump(lump).GetString().GetChars();
|
|
|
|
|
|
|
|
FString patchedCode;
|
|
|
|
patchedCode.AppendFormat("#version %d\n", 450);
|
|
|
|
patchedCode << defines;
|
|
|
|
patchedCode << "#line 1\n";
|
|
|
|
patchedCode << code;
|
|
|
|
return patchedCode;
|
2019-03-05 03:59:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VkPostprocess::RenderEffect(const FString &name)
|
|
|
|
{
|
2019-03-07 17:05:12 +00:00
|
|
|
GetVulkanFrameBuffer()->GetRenderState()->EndRenderPass();
|
|
|
|
|
2019-03-05 22:31:38 +00:00
|
|
|
if (hw_postprocess.Effects[name].Size() == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (const PPStep &step : hw_postprocess.Effects[name])
|
|
|
|
{
|
|
|
|
VkPPRenderPassKey key;
|
|
|
|
key.BlendMode = step.BlendMode;
|
|
|
|
key.InputTextures = step.Textures.Size();
|
2019-03-06 16:59:11 +00:00
|
|
|
key.Uniforms = step.Uniforms.Data.Size();
|
2019-03-05 22:31:38 +00:00
|
|
|
key.Shader = mShaders[step.ShaderName].get();
|
2019-03-08 01:24:54 +00:00
|
|
|
if (step.Output.Type == PPTextureType::PPTexture)
|
|
|
|
key.OutputFormat = mTextures[step.Output.Texture]->Format;
|
|
|
|
else
|
|
|
|
key.OutputFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
|
2019-03-05 22:31:38 +00:00
|
|
|
|
|
|
|
auto &passSetup = mRenderPassSetup[key];
|
|
|
|
if (!passSetup)
|
|
|
|
passSetup.reset(new VkPPRenderPassSetup(key));
|
|
|
|
|
|
|
|
VulkanDescriptorSet *input = GetInput(passSetup.get(), step.Textures);
|
|
|
|
VulkanFramebuffer *output = GetOutput(passSetup.get(), step.Output);
|
|
|
|
|
|
|
|
RenderScreenQuad(passSetup.get(), input, output, step.Viewport.left, step.Viewport.top, step.Viewport.width, step.Viewport.height, step.Uniforms.Data.Data(), step.Uniforms.Data.Size());
|
|
|
|
|
|
|
|
// Advance to next PP texture if our output was sent there
|
|
|
|
if (step.Output.Type == PPTextureType::NextPipelineTexture)
|
2019-03-06 18:42:02 +00:00
|
|
|
{
|
|
|
|
mCurrentPipelineImage = (mCurrentPipelineImage + 1) % VkRenderBuffers::NumPipelineImages;
|
|
|
|
}
|
2019-03-05 22:31:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VkPostprocess::RenderScreenQuad(VkPPRenderPassSetup *passSetup, VulkanDescriptorSet *descriptorSet, VulkanFramebuffer *framebuffer, int x, int y, int width, int height, const void *pushConstants, uint32_t pushConstantsSize)
|
|
|
|
{
|
|
|
|
auto fb = GetVulkanFrameBuffer();
|
|
|
|
auto cmdbuffer = fb->GetDrawCommands();
|
|
|
|
|
|
|
|
RenderPassBegin beginInfo;
|
|
|
|
beginInfo.setRenderPass(passSetup->RenderPass.get());
|
|
|
|
beginInfo.setRenderArea(x, y, width, height);
|
|
|
|
beginInfo.setFramebuffer(framebuffer);
|
|
|
|
|
|
|
|
VkViewport viewport = { };
|
|
|
|
viewport.x = x;
|
|
|
|
viewport.y = y;
|
|
|
|
viewport.width = width;
|
|
|
|
viewport.height = height;
|
|
|
|
viewport.minDepth = 0.0f;
|
|
|
|
viewport.maxDepth = 1.0f;
|
|
|
|
|
|
|
|
VkBuffer vertexBuffers[] = { static_cast<VKVertexBuffer*>(screen->mVertexData->GetBufferObjects().first)->mBuffer->buffer };
|
|
|
|
VkDeviceSize offsets[] = { 0 };
|
|
|
|
|
|
|
|
cmdbuffer->beginRenderPass(beginInfo);
|
|
|
|
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, passSetup->Pipeline.get());
|
|
|
|
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, passSetup->PipelineLayout.get(), 0, descriptorSet);
|
|
|
|
cmdbuffer->bindVertexBuffers(0, 1, vertexBuffers, offsets);
|
2019-03-06 18:42:02 +00:00
|
|
|
cmdbuffer->setViewport(0, 1, &viewport);
|
2019-03-05 22:31:38 +00:00
|
|
|
if (pushConstantsSize > 0)
|
|
|
|
cmdbuffer->pushConstants(passSetup->PipelineLayout.get(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, pushConstantsSize, pushConstants);
|
|
|
|
cmdbuffer->draw(4, 1, FFlatVertexBuffer::PRESENT_INDEX, 0);
|
|
|
|
cmdbuffer->endRenderPass();
|
2019-03-05 03:59:17 +00:00
|
|
|
}
|
|
|
|
|
2019-03-05 22:31:38 +00:00
|
|
|
VulkanDescriptorSet *VkPostprocess::GetInput(VkPPRenderPassSetup *passSetup, const TArray<PPTextureInput> &textures)
|
2019-03-05 03:59:17 +00:00
|
|
|
{
|
2019-03-06 18:42:02 +00:00
|
|
|
auto fb = GetVulkanFrameBuffer();
|
|
|
|
auto descriptors = mDescriptorPool->allocate(passSetup->DescriptorLayout.get());
|
|
|
|
|
|
|
|
WriteDescriptors write;
|
|
|
|
PipelineBarrier barrier;
|
|
|
|
bool needbarrier = false;
|
|
|
|
|
2019-03-07 17:05:12 +00:00
|
|
|
VkPipelineStageFlags srcPipelineBits = 0;
|
|
|
|
|
2019-03-06 18:42:02 +00:00
|
|
|
for (unsigned int index = 0; index < textures.Size(); index++)
|
2019-03-05 22:31:38 +00:00
|
|
|
{
|
2019-03-06 18:42:02 +00:00
|
|
|
const PPTextureInput &input = textures[index];
|
2019-03-05 22:31:38 +00:00
|
|
|
VulkanSampler *sampler = GetSampler(input.Filter, input.Wrap);
|
2019-03-06 18:42:02 +00:00
|
|
|
TextureImage tex = GetTexture(input.Type, input.Texture);
|
2019-03-05 22:31:38 +00:00
|
|
|
|
2019-03-06 18:44:48 +00:00
|
|
|
write.addCombinedImageSampler(descriptors.get(), index, tex.view, sampler, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
2019-03-06 18:42:02 +00:00
|
|
|
|
2019-03-07 17:05:12 +00:00
|
|
|
if (*tex.layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
|
2019-03-05 22:31:38 +00:00
|
|
|
{
|
2019-03-07 17:05:12 +00:00
|
|
|
srcPipelineBits |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
|
|
|
barrier.addImage(tex.image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
|
2019-03-06 18:44:48 +00:00
|
|
|
*tex.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
2019-03-06 18:42:02 +00:00
|
|
|
needbarrier = true;
|
2019-03-05 22:31:38 +00:00
|
|
|
}
|
2019-03-07 17:05:12 +00:00
|
|
|
else if (*tex.layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
|
|
|
{
|
|
|
|
srcPipelineBits |= VK_PIPELINE_STAGE_TRANSFER_BIT;
|
|
|
|
barrier.addImage(tex.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT);
|
|
|
|
*tex.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
|
|
}
|
2019-03-05 22:31:38 +00:00
|
|
|
}
|
2019-03-06 18:42:02 +00:00
|
|
|
|
|
|
|
write.updateSets(fb->device);
|
|
|
|
if (needbarrier)
|
2019-03-07 17:05:12 +00:00
|
|
|
barrier.execute(fb->GetDrawCommands(), srcPipelineBits, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
2019-03-06 18:42:02 +00:00
|
|
|
|
|
|
|
mFrameDescriptorSets.push_back(std::move(descriptors));
|
|
|
|
return mFrameDescriptorSets.back().get();
|
2019-03-05 22:31:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VulkanFramebuffer *VkPostprocess::GetOutput(VkPPRenderPassSetup *passSetup, const PPOutput &output)
|
|
|
|
{
|
2019-03-06 17:33:31 +00:00
|
|
|
auto fb = GetVulkanFrameBuffer();
|
|
|
|
|
2019-03-06 18:42:02 +00:00
|
|
|
TextureImage tex = GetTexture(output.Type, output.Texture);
|
|
|
|
|
2019-03-07 17:05:12 +00:00
|
|
|
if (tex.layout && *tex.layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
|
2019-03-06 18:42:02 +00:00
|
|
|
{
|
|
|
|
PipelineBarrier barrier;
|
2019-03-07 17:05:12 +00:00
|
|
|
barrier.addImage(tex.image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
|
2019-03-06 18:42:02 +00:00
|
|
|
barrier.execute(fb->GetDrawCommands(), VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
|
|
|
|
*tex.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
}
|
2019-03-07 17:05:12 +00:00
|
|
|
else if (tex.layout && *tex.layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
|
|
|
|
{
|
|
|
|
PipelineBarrier barrier;
|
|
|
|
barrier.addImage(tex.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT);
|
|
|
|
barrier.execute(fb->GetDrawCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);
|
|
|
|
*tex.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
}
|
2019-03-05 22:31:38 +00:00
|
|
|
|
2019-03-06 21:59:21 +00:00
|
|
|
VkImageView view;
|
|
|
|
int w, h;
|
|
|
|
if (tex.view)
|
|
|
|
{
|
|
|
|
view = tex.view->view;
|
|
|
|
w = tex.image->width;
|
|
|
|
h = tex.image->height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
view = fb->device->swapChain->swapChainImageViews[fb->device->presentImageIndex];
|
|
|
|
w = fb->device->swapChain->actualExtent.width;
|
|
|
|
h = fb->device->swapChain->actualExtent.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto &framebuffer = passSetup->Framebuffers[view];
|
2019-03-06 18:42:02 +00:00
|
|
|
if (!framebuffer)
|
|
|
|
{
|
|
|
|
FramebufferBuilder builder;
|
|
|
|
builder.setRenderPass(passSetup->RenderPass.get());
|
2019-03-06 21:59:21 +00:00
|
|
|
builder.setSize(w, h);
|
|
|
|
builder.addAttachment(view);
|
2019-03-06 18:42:02 +00:00
|
|
|
framebuffer = builder.create(GetVulkanFrameBuffer()->device);
|
|
|
|
}
|
|
|
|
|
|
|
|
return framebuffer.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
VkPostprocess::TextureImage VkPostprocess::GetTexture(const PPTextureType &type, const PPTextureName &name)
|
|
|
|
{
|
|
|
|
auto fb = GetVulkanFrameBuffer();
|
|
|
|
TextureImage tex = {};
|
|
|
|
|
|
|
|
if (type == PPTextureType::CurrentPipelineTexture || type == PPTextureType::NextPipelineTexture)
|
2019-03-06 17:33:31 +00:00
|
|
|
{
|
|
|
|
int idx = mCurrentPipelineImage;
|
2019-03-06 18:42:02 +00:00
|
|
|
if (type == PPTextureType::NextPipelineTexture)
|
2019-03-06 17:33:31 +00:00
|
|
|
idx = (idx + 1) % VkRenderBuffers::NumPipelineImages;
|
2019-03-05 22:31:38 +00:00
|
|
|
|
2019-03-06 18:42:02 +00:00
|
|
|
tex.image = fb->GetBuffers()->PipelineImage[idx].get();
|
|
|
|
tex.view = fb->GetBuffers()->PipelineView[idx].get();
|
|
|
|
tex.layout = &fb->GetBuffers()->PipelineLayout[idx];
|
2019-03-06 17:33:31 +00:00
|
|
|
}
|
2019-03-06 18:42:02 +00:00
|
|
|
else if (type == PPTextureType::PPTexture)
|
2019-03-06 17:33:31 +00:00
|
|
|
{
|
2019-03-06 18:42:02 +00:00
|
|
|
tex.image = mTextures[name]->Image.get();
|
|
|
|
tex.view = mTextures[name]->View.get();
|
|
|
|
tex.layout = &mTextures[name]->Layout;
|
2019-03-06 17:33:31 +00:00
|
|
|
}
|
2019-03-06 18:42:02 +00:00
|
|
|
else if (type == PPTextureType::SceneColor)
|
2019-03-06 17:33:31 +00:00
|
|
|
{
|
2019-03-06 18:42:02 +00:00
|
|
|
tex.image = fb->GetBuffers()->SceneColor.get();
|
|
|
|
tex.view = fb->GetBuffers()->SceneColorView.get();
|
|
|
|
tex.layout = &fb->GetBuffers()->SceneColorLayout;
|
2019-03-06 17:33:31 +00:00
|
|
|
}
|
2019-03-06 18:42:02 +00:00
|
|
|
else if (type == PPTextureType::SceneNormal)
|
2019-03-06 17:33:31 +00:00
|
|
|
{
|
2019-03-06 18:42:02 +00:00
|
|
|
tex.image = fb->GetBuffers()->SceneNormal.get();
|
|
|
|
tex.view = fb->GetBuffers()->SceneNormalView.get();
|
|
|
|
tex.layout = &fb->GetBuffers()->SceneNormalLayout;
|
2019-03-06 17:33:31 +00:00
|
|
|
}
|
2019-03-06 18:42:02 +00:00
|
|
|
else if (type == PPTextureType::SceneFog)
|
2019-03-06 17:33:31 +00:00
|
|
|
{
|
2019-03-06 18:42:02 +00:00
|
|
|
tex.image = fb->GetBuffers()->SceneFog.get();
|
|
|
|
tex.view = fb->GetBuffers()->SceneFogView.get();
|
|
|
|
tex.layout = &fb->GetBuffers()->SceneFogLayout;
|
2019-03-06 17:33:31 +00:00
|
|
|
}
|
2019-03-06 18:42:02 +00:00
|
|
|
else if (type == PPTextureType::SceneDepth)
|
2019-03-06 17:33:31 +00:00
|
|
|
{
|
2019-03-06 18:42:02 +00:00
|
|
|
tex.image = fb->GetBuffers()->SceneDepthStencil.get();
|
|
|
|
tex.view = fb->GetBuffers()->SceneDepthView.get();
|
|
|
|
tex.layout = &fb->GetBuffers()->SceneDepthStencilLayout;
|
|
|
|
}
|
2019-03-06 21:59:21 +00:00
|
|
|
else if (type == PPTextureType::SwapChain)
|
|
|
|
{
|
|
|
|
tex.image = nullptr;
|
|
|
|
tex.view = nullptr;
|
|
|
|
tex.layout = nullptr;
|
|
|
|
}
|
2019-03-06 18:42:02 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
I_FatalError("VkPostprocess::GetTexture not implemented yet for this texture type");
|
2019-03-05 22:31:38 +00:00
|
|
|
}
|
2019-03-06 17:33:31 +00:00
|
|
|
|
2019-03-06 18:42:02 +00:00
|
|
|
return tex;
|
2019-03-05 22:31:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VulkanSampler *VkPostprocess::GetSampler(PPFilterMode filter, PPWrapMode wrap)
|
|
|
|
{
|
|
|
|
int index = (((int)filter) << 2) | (int)wrap;
|
|
|
|
auto &sampler = mSamplers[index];
|
|
|
|
if (sampler)
|
|
|
|
return sampler.get();
|
|
|
|
|
|
|
|
SamplerBuilder builder;
|
|
|
|
builder.setMipmapMode(VK_SAMPLER_MIPMAP_MODE_NEAREST);
|
|
|
|
builder.setMinFilter(filter == PPFilterMode::Nearest ? VK_FILTER_NEAREST : VK_FILTER_LINEAR);
|
|
|
|
builder.setMagFilter(filter == PPFilterMode::Nearest ? VK_FILTER_NEAREST : VK_FILTER_LINEAR);
|
|
|
|
builder.setAddressMode(wrap == PPWrapMode::Clamp ? VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE : VK_SAMPLER_ADDRESS_MODE_REPEAT);
|
|
|
|
sampler = builder.create(GetVulkanFrameBuffer()->device);
|
|
|
|
return sampler.get();
|
2019-03-05 03:59:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VkPostprocess::NextEye(int eyeCount)
|
|
|
|
{
|
|
|
|
}
|
2019-03-05 22:31:38 +00:00
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
VkPPRenderPassSetup::VkPPRenderPassSetup(const VkPPRenderPassKey &key)
|
|
|
|
{
|
|
|
|
CreateDescriptorLayout(key);
|
2019-03-06 21:59:21 +00:00
|
|
|
CreatePipelineLayout(key);
|
|
|
|
CreateRenderPass(key);
|
|
|
|
CreatePipeline(key);
|
2019-03-05 22:31:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VkPPRenderPassSetup::CreateDescriptorLayout(const VkPPRenderPassKey &key)
|
|
|
|
{
|
|
|
|
DescriptorSetLayoutBuilder builder;
|
|
|
|
for (int i = 0; i < key.InputTextures; i++)
|
|
|
|
builder.addBinding(i, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
|
|
|
|
DescriptorLayout = builder.create(GetVulkanFrameBuffer()->device);
|
|
|
|
}
|
|
|
|
|
|
|
|
void VkPPRenderPassSetup::CreatePipelineLayout(const VkPPRenderPassKey &key)
|
|
|
|
{
|
|
|
|
PipelineLayoutBuilder builder;
|
|
|
|
builder.addSetLayout(DescriptorLayout.get());
|
|
|
|
if (key.Uniforms > 0)
|
|
|
|
builder.addPushConstantRange(VK_SHADER_STAGE_FRAGMENT_BIT, 0, key.Uniforms);
|
|
|
|
PipelineLayout = builder.create(GetVulkanFrameBuffer()->device);
|
|
|
|
}
|
|
|
|
|
|
|
|
void VkPPRenderPassSetup::CreatePipeline(const VkPPRenderPassKey &key)
|
|
|
|
{
|
|
|
|
GraphicsPipelineBuilder builder;
|
|
|
|
builder.addVertexShader(key.Shader->VertexShader.get());
|
|
|
|
builder.addFragmentShader(key.Shader->FragmentShader.get());
|
|
|
|
|
|
|
|
builder.addVertexBufferBinding(0, sizeof(FFlatVertex));
|
|
|
|
builder.addVertexAttribute(0, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof(FFlatVertex, x));
|
|
|
|
builder.addVertexAttribute(1, 0, VK_FORMAT_R32G32_SFLOAT, offsetof(FFlatVertex, u));
|
|
|
|
builder.addDynamicState(VK_DYNAMIC_STATE_VIEWPORT);
|
|
|
|
builder.setViewport(0.0f, 0.0f, (float)SCREENWIDTH, (float)SCREENHEIGHT);
|
|
|
|
builder.setTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
|
|
|
|
builder.setBlendMode(key.BlendMode);
|
|
|
|
builder.setLayout(PipelineLayout.get());
|
|
|
|
builder.setRenderPass(RenderPass.get());
|
|
|
|
Pipeline = builder.create(GetVulkanFrameBuffer()->device);
|
|
|
|
}
|
|
|
|
|
|
|
|
void VkPPRenderPassSetup::CreateRenderPass(const VkPPRenderPassKey &key)
|
|
|
|
{
|
|
|
|
RenderPassBuilder builder;
|
|
|
|
builder.addColorAttachment(false, key.OutputFormat, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
|
|
|
builder.addSubpass();
|
|
|
|
builder.addSubpassColorAttachmentRef(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
|
|
|
builder.addExternalSubpassDependency(
|
|
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
|
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
|
|
|
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT);
|
|
|
|
RenderPass = builder.create(GetVulkanFrameBuffer()->device);
|
|
|
|
}
|