2020-01-03 04:52:01 +00:00
|
|
|
/*
|
|
|
|
** Vulkan backend
|
|
|
|
** Copyright (c) 2016-2020 Magnus Norddahl
|
|
|
|
**
|
|
|
|
** This software is provided 'as-is', without any express or implied
|
|
|
|
** warranty. In no event will the authors be held liable for any damages
|
|
|
|
** arising from the use of this software.
|
|
|
|
**
|
|
|
|
** Permission is granted to anyone to use this software for any purpose,
|
|
|
|
** including commercial applications, and to alter it and redistribute it
|
|
|
|
** freely, subject to the following restrictions:
|
|
|
|
**
|
|
|
|
** 1. The origin of this software must not be misrepresented; you must not
|
|
|
|
** claim that you wrote the original software. If you use this software
|
|
|
|
** in a product, an acknowledgment in the product documentation would be
|
|
|
|
** appreciated but is not required.
|
|
|
|
** 2. Altered source versions must be plainly marked as such, and must not be
|
|
|
|
** misrepresented as being the original software.
|
|
|
|
** 3. This notice may not be removed or altered from any source distribution.
|
|
|
|
**
|
|
|
|
*/
|
2019-02-20 20:21:57 +00:00
|
|
|
|
|
|
|
#include "volk/volk.h"
|
|
|
|
|
|
|
|
#include "v_video.h"
|
|
|
|
#include "m_png.h"
|
|
|
|
#include "templates.h"
|
|
|
|
#include "r_videoscale.h"
|
|
|
|
#include "actor.h"
|
2019-03-01 18:01:06 +00:00
|
|
|
#include "i_time.h"
|
2019-03-05 03:59:17 +00:00
|
|
|
#include "g_game.h"
|
2020-04-11 17:43:17 +00:00
|
|
|
#include "v_text.h"
|
2019-02-20 20:21:57 +00:00
|
|
|
|
|
|
|
#include "hwrenderer/utility/hw_clock.h"
|
|
|
|
#include "hwrenderer/utility/hw_vrmodes.h"
|
2019-03-16 22:37:38 +00:00
|
|
|
#include "hwrenderer/utility/hw_cvars.h"
|
2019-02-21 11:31:14 +00:00
|
|
|
#include "hwrenderer/models/hw_models.h"
|
|
|
|
#include "hwrenderer/scene/hw_skydome.h"
|
2019-03-01 18:01:06 +00:00
|
|
|
#include "hwrenderer/scene/hw_fakeflat.h"
|
|
|
|
#include "hwrenderer/scene/hw_drawinfo.h"
|
|
|
|
#include "hwrenderer/scene/hw_portal.h"
|
2019-02-21 11:31:14 +00:00
|
|
|
#include "hwrenderer/data/hw_viewpointbuffer.h"
|
|
|
|
#include "hwrenderer/data/flatvertices.h"
|
2019-02-26 10:27:29 +00:00
|
|
|
#include "hwrenderer/data/shaderuniforms.h"
|
2019-02-21 11:31:14 +00:00
|
|
|
#include "hwrenderer/dynlights/hw_lightbuffer.h"
|
2019-02-20 20:21:57 +00:00
|
|
|
|
2019-02-27 14:37:37 +00:00
|
|
|
#include "swrenderer/r_swscene.h"
|
|
|
|
|
2019-02-20 20:21:57 +00:00
|
|
|
#include "vk_framebuffer.h"
|
2019-02-21 11:31:14 +00:00
|
|
|
#include "vk_buffers.h"
|
2019-02-21 21:49:00 +00:00
|
|
|
#include "vulkan/renderer/vk_renderstate.h"
|
2019-02-26 10:27:29 +00:00
|
|
|
#include "vulkan/renderer/vk_renderpass.h"
|
2019-07-28 14:28:43 +00:00
|
|
|
#include "vulkan/renderer/vk_streambuffer.h"
|
2019-03-05 03:59:17 +00:00
|
|
|
#include "vulkan/renderer/vk_postprocess.h"
|
|
|
|
#include "vulkan/renderer/vk_renderbuffers.h"
|
2019-02-21 21:49:00 +00:00
|
|
|
#include "vulkan/shaders/vk_shader.h"
|
2019-02-20 20:21:57 +00:00
|
|
|
#include "vulkan/textures/vk_samplers.h"
|
2019-02-26 14:29:08 +00:00
|
|
|
#include "vulkan/textures/vk_hwtexture.h"
|
2019-02-20 23:25:51 +00:00
|
|
|
#include "vulkan/system/vk_builders.h"
|
2019-02-21 09:19:59 +00:00
|
|
|
#include "vulkan/system/vk_swapchain.h"
|
2020-04-11 10:56:55 +00:00
|
|
|
#include "engineerrors.h"
|
2019-02-20 23:25:51 +00:00
|
|
|
|
2019-02-21 21:49:00 +00:00
|
|
|
void Draw2D(F2DDrawer *drawer, FRenderState &state);
|
2019-03-14 22:07:52 +00:00
|
|
|
void DoWriteSavePic(FileWriter *file, ESSType ssformat, uint8_t *scr, int width, int height, sector_t *viewsector, bool upsidedown);
|
2019-02-20 20:21:57 +00:00
|
|
|
|
|
|
|
EXTERN_CVAR(Bool, r_drawvoxels)
|
|
|
|
EXTERN_CVAR(Int, gl_tonemap)
|
2019-03-01 18:01:06 +00:00
|
|
|
EXTERN_CVAR(Int, screenblocks)
|
|
|
|
EXTERN_CVAR(Bool, cl_capfps)
|
2019-03-04 14:55:43 +00:00
|
|
|
EXTERN_CVAR(Bool, gl_no_skyclear)
|
2019-03-01 18:01:06 +00:00
|
|
|
|
|
|
|
extern bool NoInterpolateView;
|
2019-04-19 20:42:32 +00:00
|
|
|
extern int rendered_commandbuffers;
|
|
|
|
int current_rendered_commandbuffers;
|
2019-02-20 20:21:57 +00:00
|
|
|
|
2019-04-30 20:55:35 +00:00
|
|
|
extern bool gpuStatActive;
|
|
|
|
extern bool keepGpuStatActive;
|
|
|
|
extern FString gpuStatOutput;
|
|
|
|
|
2019-02-20 20:21:57 +00:00
|
|
|
VulkanFrameBuffer::VulkanFrameBuffer(void *hMonitor, bool fullscreen, VulkanDevice *dev) :
|
|
|
|
Super(hMonitor, fullscreen)
|
|
|
|
{
|
2019-02-21 09:19:59 +00:00
|
|
|
device = dev;
|
2019-03-14 22:33:19 +00:00
|
|
|
|
|
|
|
swapChain = std::make_unique<VulkanSwapChain>(device);
|
|
|
|
mSwapChainImageAvailableSemaphore.reset(new VulkanSemaphore(device));
|
|
|
|
mRenderFinishedSemaphore.reset(new VulkanSemaphore(device));
|
|
|
|
|
2019-04-19 18:55:15 +00:00
|
|
|
for (auto &semaphore : mSubmitSemaphore)
|
|
|
|
semaphore.reset(new VulkanSemaphore(device));
|
|
|
|
|
|
|
|
for (auto &fence : mSubmitFence)
|
|
|
|
fence.reset(new VulkanFence(device));
|
|
|
|
|
2019-04-19 21:56:54 +00:00
|
|
|
for (int i = 0; i < maxConcurrentSubmitCount; i++)
|
|
|
|
mSubmitWaitFences[i] = mSubmitFence[i]->fence;
|
2019-02-21 09:19:59 +00:00
|
|
|
}
|
2019-02-20 23:25:51 +00:00
|
|
|
|
2019-02-21 09:19:59 +00:00
|
|
|
VulkanFrameBuffer::~VulkanFrameBuffer()
|
|
|
|
{
|
2019-08-08 18:56:11 +00:00
|
|
|
vkDeviceWaitIdle(device->device); // make sure the GPU is no longer using any objects before RAII tears them down
|
|
|
|
|
2019-04-30 18:02:00 +00:00
|
|
|
// screen is already null at this point, but VkHardwareTexture::ResetAll needs it during clean up. Is there a better way we can do this?
|
|
|
|
auto tmp = screen;
|
|
|
|
screen = this;
|
|
|
|
|
2019-03-01 17:31:33 +00:00
|
|
|
// All descriptors must be destroyed before the descriptor pool in renderpass manager is destroyed
|
2019-04-30 17:49:29 +00:00
|
|
|
VkHardwareTexture::ResetAll();
|
|
|
|
VKBuffer::ResetAll();
|
2019-03-15 22:24:31 +00:00
|
|
|
PPResource::ResetAll();
|
|
|
|
|
2019-07-28 14:28:43 +00:00
|
|
|
delete MatrixBuffer;
|
|
|
|
delete StreamBuffer;
|
2019-02-26 19:19:54 +00:00
|
|
|
delete mVertexData;
|
|
|
|
delete mSkyData;
|
|
|
|
delete mViewpoints;
|
|
|
|
delete mLights;
|
2019-03-15 06:54:34 +00:00
|
|
|
mShadowMap.Reset();
|
2019-03-17 19:36:08 +00:00
|
|
|
|
2019-04-30 18:02:00 +00:00
|
|
|
screen = tmp;
|
|
|
|
|
2019-03-17 19:36:08 +00:00
|
|
|
DeleteFrameObjects();
|
2019-02-21 09:19:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VulkanFrameBuffer::InitializeState()
|
|
|
|
{
|
2019-03-03 13:32:03 +00:00
|
|
|
static bool first = true;
|
|
|
|
if (first)
|
|
|
|
{
|
|
|
|
PrintStartupLog();
|
|
|
|
first = false;
|
|
|
|
}
|
|
|
|
|
2019-08-11 19:16:09 +00:00
|
|
|
// Use the same names here as OpenGL returns.
|
2019-06-04 09:42:01 +00:00
|
|
|
switch (device->PhysicalDevice.Properties.vendorID)
|
|
|
|
{
|
2019-08-11 19:16:09 +00:00
|
|
|
case 0x1002: vendorstring = "ATI Technologies Inc."; break;
|
|
|
|
case 0x10DE: vendorstring = "NVIDIA Corporation"; break;
|
2019-06-04 09:42:01 +00:00
|
|
|
case 0x8086: vendorstring = "Intel"; break;
|
|
|
|
default: vendorstring = "Unknown"; break;
|
|
|
|
}
|
|
|
|
|
2019-02-26 19:19:54 +00:00
|
|
|
hwcaps = RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE;
|
2019-03-05 18:49:06 +00:00
|
|
|
glslversion = 4.50f;
|
2019-03-09 09:20:14 +00:00
|
|
|
uniformblockalignment = (unsigned int)device->PhysicalDevice.Properties.limits.minUniformBufferOffsetAlignment;
|
|
|
|
maxuniformblock = device->PhysicalDevice.Properties.limits.maxUniformBufferRange;
|
2019-02-26 19:19:54 +00:00
|
|
|
|
2019-04-04 13:58:48 +00:00
|
|
|
mCommandPool.reset(new VulkanCommandPool(device, device->graphicsFamily));
|
2019-02-21 11:31:14 +00:00
|
|
|
|
2019-03-05 03:59:17 +00:00
|
|
|
mScreenBuffers.reset(new VkRenderBuffers());
|
|
|
|
mSaveBuffers.reset(new VkRenderBuffers());
|
|
|
|
mActiveRenderBuffers = mScreenBuffers.get();
|
|
|
|
|
|
|
|
mPostprocess.reset(new VkPostprocess());
|
2019-03-01 14:37:13 +00:00
|
|
|
mRenderPassManager.reset(new VkRenderPassManager());
|
|
|
|
|
2019-02-21 11:31:14 +00:00
|
|
|
mVertexData = new FFlatVertexBuffer(GetWidth(), GetHeight());
|
|
|
|
mSkyData = new FSkyVertexBuffer;
|
2019-06-10 08:50:17 +00:00
|
|
|
mViewpoints = new HWViewpointBuffer;
|
2019-02-21 11:31:14 +00:00
|
|
|
mLights = new FLightBuffer();
|
|
|
|
|
2019-03-08 20:34:21 +00:00
|
|
|
CreateFanToTrisIndexBuffer();
|
|
|
|
|
2019-02-26 15:50:54 +00:00
|
|
|
// To do: move this to HW renderer interface maybe?
|
2019-07-28 15:04:56 +00:00
|
|
|
MatrixBuffer = new VkStreamBuffer(sizeof(MatricesUBO), 50000);
|
2019-07-28 17:05:39 +00:00
|
|
|
StreamBuffer = new VkStreamBuffer(sizeof(StreamUBO), 300);
|
2019-02-26 15:50:54 +00:00
|
|
|
|
2019-02-22 10:30:48 +00:00
|
|
|
mShaderManager.reset(new VkShaderManager(device));
|
2019-02-21 09:19:59 +00:00
|
|
|
mSamplerManager.reset(new VkSamplerManager(device));
|
2019-03-01 14:37:13 +00:00
|
|
|
mRenderPassManager->Init();
|
2019-03-08 20:34:21 +00:00
|
|
|
#ifdef __APPLE__
|
|
|
|
mRenderState.reset(new VkRenderStateMolten());
|
|
|
|
#else
|
2019-02-21 21:49:00 +00:00
|
|
|
mRenderState.reset(new VkRenderState());
|
2019-03-08 20:34:21 +00:00
|
|
|
#endif
|
2019-04-30 20:55:35 +00:00
|
|
|
|
2019-07-03 14:49:06 +00:00
|
|
|
if (device->graphicsTimeQueries)
|
|
|
|
{
|
|
|
|
QueryPoolBuilder querybuilder;
|
|
|
|
querybuilder.setQueryType(VK_QUERY_TYPE_TIMESTAMP, MaxTimestampQueries);
|
|
|
|
mTimestampQueryPool = querybuilder.create(device);
|
2019-04-30 20:55:35 +00:00
|
|
|
|
2019-07-03 14:49:06 +00:00
|
|
|
GetDrawCommands()->resetQueryPool(mTimestampQueryPool.get(), 0, MaxTimestampQueries);
|
|
|
|
}
|
2019-02-20 20:21:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VulkanFrameBuffer::Update()
|
|
|
|
{
|
|
|
|
twoD.Reset();
|
|
|
|
Flush3D.Reset();
|
|
|
|
|
|
|
|
Flush3D.Clock();
|
|
|
|
|
2019-03-07 17:05:12 +00:00
|
|
|
GetPostprocess()->SetActiveRenderTarget();
|
|
|
|
|
2019-02-21 09:19:59 +00:00
|
|
|
Draw2D();
|
|
|
|
Clear2D();
|
2019-02-26 10:27:29 +00:00
|
|
|
|
|
|
|
mRenderState->EndRenderPass();
|
2019-03-07 17:05:12 +00:00
|
|
|
mRenderState->EndFrame();
|
2019-02-26 10:27:29 +00:00
|
|
|
|
2019-03-13 13:10:13 +00:00
|
|
|
Flush3D.Unclock();
|
|
|
|
|
2019-04-19 18:55:15 +00:00
|
|
|
WaitForCommands(true);
|
2019-04-30 20:55:35 +00:00
|
|
|
UpdateGpuStats();
|
2019-03-13 13:10:13 +00:00
|
|
|
|
|
|
|
Super::Update();
|
|
|
|
}
|
|
|
|
|
2019-03-17 19:36:08 +00:00
|
|
|
void VulkanFrameBuffer::DeleteFrameObjects()
|
|
|
|
{
|
|
|
|
FrameDeleteList.Images.clear();
|
|
|
|
FrameDeleteList.ImageViews.clear();
|
2019-06-10 20:14:02 +00:00
|
|
|
FrameDeleteList.Framebuffers.clear();
|
2019-03-17 19:36:08 +00:00
|
|
|
FrameDeleteList.Buffers.clear();
|
|
|
|
FrameDeleteList.Descriptors.clear();
|
2019-04-30 14:47:11 +00:00
|
|
|
FrameDeleteList.DescriptorPools.clear();
|
2019-04-19 18:55:15 +00:00
|
|
|
FrameDeleteList.CommandBuffers.clear();
|
2019-03-17 19:36:08 +00:00
|
|
|
}
|
|
|
|
|
2019-04-20 14:42:52 +00:00
|
|
|
void VulkanFrameBuffer::FlushCommands(VulkanCommandBuffer **commands, size_t count, bool finish, bool lastsubmit)
|
2019-04-19 20:42:32 +00:00
|
|
|
{
|
2019-04-19 21:56:54 +00:00
|
|
|
int currentIndex = mNextSubmit % maxConcurrentSubmitCount;
|
2019-04-19 18:55:15 +00:00
|
|
|
|
2019-04-19 21:56:54 +00:00
|
|
|
if (mNextSubmit >= maxConcurrentSubmitCount)
|
2019-04-19 20:42:32 +00:00
|
|
|
{
|
|
|
|
vkWaitForFences(device->device, 1, &mSubmitFence[currentIndex]->fence, VK_TRUE, std::numeric_limits<uint64_t>::max());
|
|
|
|
vkResetFences(device->device, 1, &mSubmitFence[currentIndex]->fence);
|
|
|
|
}
|
|
|
|
|
|
|
|
QueueSubmit submit;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < count; i++)
|
|
|
|
submit.addCommandBuffer(commands[i]);
|
|
|
|
|
2019-04-19 21:56:54 +00:00
|
|
|
if (mNextSubmit > 0)
|
|
|
|
submit.addWait(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, mSubmitSemaphore[(mNextSubmit - 1) % maxConcurrentSubmitCount].get());
|
2019-04-19 20:42:32 +00:00
|
|
|
|
2019-04-19 21:26:06 +00:00
|
|
|
if (finish && presentImageIndex != 0xffffffff)
|
|
|
|
{
|
|
|
|
submit.addWait(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, mSwapChainImageAvailableSemaphore.get());
|
|
|
|
submit.addSignal(mRenderFinishedSemaphore.get());
|
|
|
|
}
|
|
|
|
|
2019-04-20 14:42:52 +00:00
|
|
|
if (!lastsubmit)
|
2019-04-20 14:35:26 +00:00
|
|
|
submit.addSignal(mSubmitSemaphore[currentIndex].get());
|
|
|
|
|
2019-04-19 20:42:32 +00:00
|
|
|
submit.execute(device, device->graphicsQueue, mSubmitFence[currentIndex].get());
|
2019-04-19 21:56:54 +00:00
|
|
|
mNextSubmit++;
|
2019-04-19 20:42:32 +00:00
|
|
|
}
|
|
|
|
|
2019-04-20 14:42:52 +00:00
|
|
|
void VulkanFrameBuffer::FlushCommands(bool finish, bool lastsubmit)
|
2019-04-19 20:42:32 +00:00
|
|
|
{
|
2019-08-25 10:12:29 +00:00
|
|
|
mRenderState->EndRenderPass();
|
|
|
|
|
2019-04-19 20:42:32 +00:00
|
|
|
if (mDrawCommands || mTransferCommands)
|
|
|
|
{
|
|
|
|
VulkanCommandBuffer *commands[2];
|
|
|
|
size_t count = 0;
|
2019-04-19 18:55:15 +00:00
|
|
|
|
|
|
|
if (mTransferCommands)
|
|
|
|
{
|
|
|
|
mTransferCommands->end();
|
2019-04-19 20:42:32 +00:00
|
|
|
commands[count++] = mTransferCommands.get();
|
2019-04-19 18:55:15 +00:00
|
|
|
FrameDeleteList.CommandBuffers.push_back(std::move(mTransferCommands));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mDrawCommands)
|
|
|
|
{
|
|
|
|
mDrawCommands->end();
|
2019-04-19 20:42:32 +00:00
|
|
|
commands[count++] = mDrawCommands.get();
|
2019-04-19 18:55:15 +00:00
|
|
|
FrameDeleteList.CommandBuffers.push_back(std::move(mDrawCommands));
|
|
|
|
}
|
|
|
|
|
2019-04-20 14:42:52 +00:00
|
|
|
FlushCommands(commands, count, finish, lastsubmit);
|
2019-04-19 18:55:15 +00:00
|
|
|
|
2019-04-19 21:26:06 +00:00
|
|
|
current_rendered_commandbuffers += (int)count;
|
2019-04-19 18:55:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VulkanFrameBuffer::WaitForCommands(bool finish)
|
2019-03-13 13:10:13 +00:00
|
|
|
{
|
2019-04-11 02:52:57 +00:00
|
|
|
if (finish)
|
|
|
|
{
|
|
|
|
Finish.Reset();
|
|
|
|
Finish.Clock();
|
|
|
|
|
|
|
|
presentImageIndex = swapChain->AcquireImage(GetClientWidth(), GetClientHeight(), mSwapChainImageAvailableSemaphore.get());
|
|
|
|
if (presentImageIndex != 0xffffffff)
|
2019-05-20 10:36:45 +00:00
|
|
|
mPostprocess->DrawPresentTexture(mOutputLetterbox, true, false);
|
2019-04-11 02:52:57 +00:00
|
|
|
}
|
|
|
|
|
2019-04-20 14:42:52 +00:00
|
|
|
FlushCommands(finish, true);
|
2019-03-14 22:07:52 +00:00
|
|
|
|
2019-04-11 02:52:57 +00:00
|
|
|
if (finish)
|
|
|
|
{
|
|
|
|
FPSLimit();
|
|
|
|
|
|
|
|
if (presentImageIndex != 0xffffffff)
|
|
|
|
swapChain->QueuePresent(presentImageIndex, mRenderFinishedSemaphore.get());
|
|
|
|
}
|
|
|
|
|
2019-04-19 21:56:54 +00:00
|
|
|
int numWaitFences = MIN(mNextSubmit, (int)maxConcurrentSubmitCount);
|
2019-05-01 10:42:07 +00:00
|
|
|
|
|
|
|
if (numWaitFences > 0)
|
|
|
|
{
|
|
|
|
vkWaitForFences(device->device, numWaitFences, mSubmitWaitFences, VK_TRUE, std::numeric_limits<uint64_t>::max());
|
|
|
|
vkResetFences(device->device, numWaitFences, mSubmitWaitFences);
|
|
|
|
}
|
2019-04-19 18:55:15 +00:00
|
|
|
|
2019-04-11 02:52:57 +00:00
|
|
|
DeleteFrameObjects();
|
2019-04-19 21:56:54 +00:00
|
|
|
mNextSubmit = 0;
|
2019-04-11 02:52:57 +00:00
|
|
|
|
|
|
|
if (finish)
|
2019-03-14 22:07:52 +00:00
|
|
|
{
|
2019-04-11 02:52:57 +00:00
|
|
|
Finish.Unclock();
|
2019-04-19 20:42:32 +00:00
|
|
|
rendered_commandbuffers = current_rendered_commandbuffers;
|
|
|
|
current_rendered_commandbuffers = 0;
|
2019-03-14 22:07:52 +00:00
|
|
|
}
|
2019-02-20 20:21:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VulkanFrameBuffer::WriteSavePic(player_t *player, FileWriter *file, int width, int height)
|
|
|
|
{
|
|
|
|
if (!V_IsHardwareRenderer())
|
2019-03-14 22:07:52 +00:00
|
|
|
{
|
2019-02-20 20:21:57 +00:00
|
|
|
Super::WriteSavePic(player, file, width, height);
|
2019-03-14 22:07:52 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
IntRect bounds;
|
|
|
|
bounds.left = 0;
|
|
|
|
bounds.top = 0;
|
|
|
|
bounds.width = width;
|
|
|
|
bounds.height = height;
|
|
|
|
|
|
|
|
// we must be sure the GPU finished reading from the buffer before we fill it with new data.
|
2019-04-19 18:55:15 +00:00
|
|
|
WaitForCommands(false);
|
2019-03-14 22:07:52 +00:00
|
|
|
|
|
|
|
// Switch to render buffers dimensioned for the savepic
|
|
|
|
mActiveRenderBuffers = mSaveBuffers.get();
|
|
|
|
|
2019-05-15 14:11:43 +00:00
|
|
|
mPostprocess->ImageTransitionScene(true);
|
|
|
|
|
2019-03-14 22:07:52 +00:00
|
|
|
hw_ClearFakeFlat();
|
|
|
|
GetRenderState()->SetVertexBuffer(screen->mVertexData);
|
|
|
|
screen->mVertexData->Reset();
|
|
|
|
screen->mLights->Clear();
|
|
|
|
screen->mViewpoints->Clear();
|
|
|
|
|
|
|
|
// This shouldn't overwrite the global viewpoint even for a short time.
|
|
|
|
FRenderViewpoint savevp;
|
|
|
|
sector_t *viewsector = RenderViewpoint(savevp, players[consoleplayer].camera, &bounds, r_viewpoint.FieldOfView.Degrees, 1.6f, 1.6f, true, false);
|
|
|
|
GetRenderState()->EnableStencil(false);
|
|
|
|
GetRenderState()->SetNoSoftLightLevel();
|
|
|
|
|
|
|
|
int numpixels = width * height;
|
|
|
|
uint8_t * scr = (uint8_t *)M_Malloc(numpixels * 3);
|
|
|
|
CopyScreenToBuffer(width, height, scr);
|
|
|
|
|
|
|
|
DoWriteSavePic(file, SS_RGB, scr, width, height, viewsector, false);
|
|
|
|
M_Free(scr);
|
|
|
|
|
|
|
|
// Switch back the screen render buffers
|
|
|
|
screen->SetViewportRects(nullptr);
|
|
|
|
mActiveRenderBuffers = mScreenBuffers.get();
|
|
|
|
}
|
2019-02-20 20:21:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sector_t *VulkanFrameBuffer::RenderView(player_t *player)
|
|
|
|
{
|
2019-03-01 18:01:06 +00:00
|
|
|
// To do: this is virtually identical to FGLRenderer::RenderView and should be merged.
|
|
|
|
|
2019-02-27 14:37:37 +00:00
|
|
|
mRenderState->SetVertexBuffer(screen->mVertexData);
|
|
|
|
screen->mVertexData->Reset();
|
|
|
|
|
2019-03-01 18:01:06 +00:00
|
|
|
sector_t *retsec;
|
2019-02-27 14:37:37 +00:00
|
|
|
if (!V_IsHardwareRenderer())
|
|
|
|
{
|
2019-05-03 14:05:41 +00:00
|
|
|
mPostprocess->SetActiveRenderTarget();
|
|
|
|
|
2019-02-27 14:37:37 +00:00
|
|
|
if (!swdrawer) swdrawer.reset(new SWSceneDrawer);
|
2019-03-01 18:01:06 +00:00
|
|
|
retsec = swdrawer->RenderView(player);
|
2019-02-27 14:37:37 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-03-01 18:01:06 +00:00
|
|
|
hw_ClearFakeFlat();
|
|
|
|
|
|
|
|
iter_dlightf = iter_dlight = draw_dlight = draw_dlightf = 0;
|
|
|
|
|
|
|
|
checkBenchActive();
|
|
|
|
|
|
|
|
// reset statistics counters
|
|
|
|
ResetProfilingData();
|
|
|
|
|
|
|
|
// Get this before everything else
|
|
|
|
if (cl_capfps || r_NoInterpolate) r_viewpoint.TicFrac = 1.;
|
|
|
|
else r_viewpoint.TicFrac = I_GetTimeFrac();
|
|
|
|
|
|
|
|
screen->mLights->Clear();
|
|
|
|
screen->mViewpoints->Clear();
|
|
|
|
|
|
|
|
// NoInterpolateView should have no bearing on camera textures, but needs to be preserved for the main view below.
|
|
|
|
bool saved_niv = NoInterpolateView;
|
|
|
|
NoInterpolateView = false;
|
|
|
|
|
|
|
|
// Shader start time does not need to be handled per level. Just use the one from the camera to render from.
|
2020-03-26 11:45:27 +00:00
|
|
|
if (player->camera)
|
|
|
|
GetRenderState()->CheckTimer(player->camera->Level->ShaderStartTime);
|
2019-03-01 18:01:06 +00:00
|
|
|
// prepare all camera textures that have been used in the last frame.
|
|
|
|
// This must be done for all levels, not just the primary one!
|
|
|
|
for (auto Level : AllLevels())
|
|
|
|
{
|
|
|
|
Level->canvasTextureInfo.UpdateAll([&](AActor *camera, FCanvasTexture *camtex, double fov)
|
|
|
|
{
|
|
|
|
RenderTextureView(camtex, camera, fov);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
NoInterpolateView = saved_niv;
|
|
|
|
|
|
|
|
// now render the main view
|
|
|
|
float fovratio;
|
|
|
|
float ratio = r_viewwindow.WidescreenRatio;
|
|
|
|
if (r_viewwindow.WidescreenRatio >= 1.3f)
|
|
|
|
{
|
|
|
|
fovratio = 1.333333f;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fovratio = ratio;
|
|
|
|
}
|
|
|
|
|
2019-03-16 22:37:38 +00:00
|
|
|
mPostprocess->ImageTransitionScene(true); // This is the only line that differs compared to FGLRenderer::RenderView
|
|
|
|
|
2019-03-01 18:01:06 +00:00
|
|
|
retsec = RenderViewpoint(r_viewpoint, player->camera, NULL, r_viewpoint.FieldOfView.Degrees, ratio, fovratio, true, true);
|
|
|
|
}
|
|
|
|
All.Unclock();
|
|
|
|
return retsec;
|
|
|
|
}
|
|
|
|
|
|
|
|
sector_t *VulkanFrameBuffer::RenderViewpoint(FRenderViewpoint &mainvp, AActor * camera, IntRect * bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen)
|
|
|
|
{
|
|
|
|
// To do: this is virtually identical to FGLRenderer::RenderViewpoint and should be merged.
|
|
|
|
|
|
|
|
R_SetupFrame(mainvp, r_viewwindow, camera);
|
|
|
|
|
|
|
|
if (mainview && toscreen)
|
|
|
|
UpdateShadowMap();
|
|
|
|
|
|
|
|
// Update the attenuation flag of all light defaults for each viewpoint.
|
|
|
|
// This function will only do something if the setting differs.
|
|
|
|
FLightDefaults::SetAttenuationForLevel(!!(camera->Level->flags3 & LEVEL3_ATTENUATE));
|
|
|
|
|
|
|
|
// Render (potentially) multiple views for stereo 3d
|
|
|
|
// Fixme. The view offsetting should be done with a static table and not require setup of the entire render state for the mode.
|
|
|
|
auto vrmode = VRMode::GetVRMode(mainview && toscreen);
|
|
|
|
for (int eye_ix = 0; eye_ix < vrmode->mEyeCount; ++eye_ix)
|
|
|
|
{
|
|
|
|
const auto &eye = vrmode->mEyes[eye_ix];
|
|
|
|
screen->SetViewportRects(bounds);
|
|
|
|
|
|
|
|
if (mainview) // Bind the scene frame buffer and turn on draw buffers used by ssao
|
|
|
|
{
|
2019-06-10 20:14:02 +00:00
|
|
|
mRenderState->SetRenderTarget(&GetBuffers()->SceneColor, GetBuffers()->SceneDepthStencil.View.get(), GetBuffers()->GetWidth(), GetBuffers()->GetHeight(), VK_FORMAT_R16G16B16A16_SFLOAT, GetBuffers()->GetSceneSamples());
|
2019-03-01 18:01:06 +00:00
|
|
|
bool useSSAO = (gl_ssao != 0);
|
|
|
|
GetRenderState()->SetPassType(useSSAO ? GBUFFER_PASS : NORMAL_PASS);
|
2019-03-16 22:37:38 +00:00
|
|
|
GetRenderState()->EnableDrawBuffers(GetRenderState()->GetPassDrawBufferCount());
|
2019-03-07 17:05:12 +00:00
|
|
|
}
|
2019-03-01 18:01:06 +00:00
|
|
|
|
|
|
|
auto di = HWDrawInfo::StartDrawInfo(mainvp.ViewLevel, nullptr, mainvp, nullptr);
|
|
|
|
auto &vp = di->Viewpoint;
|
|
|
|
|
|
|
|
di->Set3DViewport(*GetRenderState());
|
|
|
|
di->SetViewArea();
|
|
|
|
auto cm = di->SetFullbrightFlags(mainview ? vp.camera->player : nullptr);
|
|
|
|
di->Viewpoint.FieldOfView = fov; // Set the real FOV for the current scene (it's not necessarily the same as the global setting in r_viewpoint)
|
|
|
|
|
|
|
|
// Stereo mode specific perspective projection
|
|
|
|
di->VPUniforms.mProjectionMatrix = eye.GetProjection(fov, ratio, fovratio);
|
|
|
|
// Stereo mode specific viewpoint adjustment
|
|
|
|
vp.Pos += eye.GetViewShift(vp.HWAngles.Yaw.Degrees);
|
|
|
|
di->SetupView(*GetRenderState(), vp.Pos.X, vp.Pos.Y, vp.Pos.Z, false, false);
|
|
|
|
|
|
|
|
// std::function until this can be done better in a cross-API fashion.
|
|
|
|
di->ProcessScene(toscreen, [&](HWDrawInfo *di, int mode) {
|
|
|
|
DrawScene(di, mode);
|
|
|
|
});
|
|
|
|
|
|
|
|
if (mainview)
|
|
|
|
{
|
|
|
|
PostProcess.Clock();
|
|
|
|
if (toscreen) di->EndDrawScene(mainvp.sector, *GetRenderState()); // do not call this for camera textures.
|
|
|
|
|
|
|
|
if (GetRenderState()->GetPassType() == GBUFFER_PASS) // Turn off ssao draw buffers
|
|
|
|
{
|
|
|
|
GetRenderState()->SetPassType(NORMAL_PASS);
|
|
|
|
GetRenderState()->EnableDrawBuffers(1);
|
|
|
|
}
|
|
|
|
|
2019-03-16 22:37:38 +00:00
|
|
|
mPostprocess->BlitSceneToPostprocess(); // Copy the resulting scene to the current post process texture
|
2019-03-01 18:01:06 +00:00
|
|
|
|
|
|
|
PostProcessScene(cm, [&]() { di->DrawEndScene2D(mainvp.sector, *GetRenderState()); });
|
|
|
|
|
|
|
|
PostProcess.Unclock();
|
|
|
|
}
|
|
|
|
di->EndDrawInfo();
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (vrmode->mEyeCount > 1)
|
|
|
|
mBuffers->BlitToEyeTexture(eye_ix);
|
|
|
|
#endif
|
2019-02-27 14:37:37 +00:00
|
|
|
}
|
2019-03-01 18:01:06 +00:00
|
|
|
|
|
|
|
return mainvp.sector;
|
|
|
|
}
|
|
|
|
|
2019-03-13 10:14:42 +00:00
|
|
|
void VulkanFrameBuffer::RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, double FOV)
|
|
|
|
{
|
2020-04-16 21:37:22 +00:00
|
|
|
auto BaseLayer = static_cast<VkHardwareTexture*>(tex->GetHardwareTexture(0, 0));
|
2019-03-13 10:14:42 +00:00
|
|
|
|
2020-04-17 19:32:09 +00:00
|
|
|
float ratio = tex->aspectRatio;
|
2019-05-16 16:26:47 +00:00
|
|
|
VkTextureImage *image = BaseLayer->GetImage(tex, 0, 0);
|
|
|
|
VkTextureImage *depthStencil = BaseLayer->GetDepthStencil(tex);
|
2019-03-13 10:14:42 +00:00
|
|
|
|
|
|
|
mRenderState->EndRenderPass();
|
|
|
|
|
2019-05-16 16:26:47 +00:00
|
|
|
VkImageTransition barrier0;
|
|
|
|
barrier0.addImage(image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true);
|
|
|
|
barrier0.execute(GetDrawCommands());
|
2019-03-13 10:14:42 +00:00
|
|
|
|
2019-06-10 20:14:02 +00:00
|
|
|
mRenderState->SetRenderTarget(image, depthStencil->View.get(), image->Image->width, image->Image->height, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT);
|
2019-03-13 10:14:42 +00:00
|
|
|
|
|
|
|
IntRect bounds;
|
|
|
|
bounds.left = bounds.top = 0;
|
2020-04-17 19:32:09 +00:00
|
|
|
bounds.width = std::min(tex->GetWidth(), image->Image->width);
|
|
|
|
bounds.height = std::min(tex->GetHeight(), image->Image->height);
|
2019-03-13 10:14:42 +00:00
|
|
|
|
|
|
|
FRenderViewpoint texvp;
|
2020-04-14 22:48:04 +00:00
|
|
|
RenderViewpoint(texvp, Viewpoint, &bounds, FOV, ratio, ratio, false, false);
|
2019-03-13 10:14:42 +00:00
|
|
|
|
|
|
|
mRenderState->EndRenderPass();
|
|
|
|
|
2019-05-16 16:26:47 +00:00
|
|
|
VkImageTransition barrier1;
|
|
|
|
barrier1.addImage(image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, false);
|
|
|
|
barrier1.execute(GetDrawCommands());
|
2019-03-13 10:14:42 +00:00
|
|
|
|
2019-06-10 20:14:02 +00:00
|
|
|
mRenderState->SetRenderTarget(&GetBuffers()->SceneColor, GetBuffers()->SceneDepthStencil.View.get(), GetBuffers()->GetWidth(), GetBuffers()->GetHeight(), VK_FORMAT_R16G16B16A16_SFLOAT, GetBuffers()->GetSceneSamples());
|
2019-03-13 10:14:42 +00:00
|
|
|
|
|
|
|
tex->SetUpdated(true);
|
|
|
|
}
|
|
|
|
|
2019-03-01 18:01:06 +00:00
|
|
|
void VulkanFrameBuffer::DrawScene(HWDrawInfo *di, int drawmode)
|
|
|
|
{
|
|
|
|
// To do: this is virtually identical to FGLRenderer::DrawScene and should be merged.
|
|
|
|
|
|
|
|
static int recursion = 0;
|
|
|
|
static int ssao_portals_available = 0;
|
|
|
|
const auto &vp = di->Viewpoint;
|
|
|
|
|
|
|
|
bool applySSAO = false;
|
|
|
|
if (drawmode == DM_MAINVIEW)
|
|
|
|
{
|
|
|
|
ssao_portals_available = gl_ssao_portals;
|
|
|
|
applySSAO = true;
|
|
|
|
}
|
|
|
|
else if (drawmode == DM_OFFSCREEN)
|
|
|
|
{
|
|
|
|
ssao_portals_available = 0;
|
|
|
|
}
|
|
|
|
else if (drawmode == DM_PORTAL && ssao_portals_available > 0)
|
|
|
|
{
|
|
|
|
applySSAO = true;
|
|
|
|
ssao_portals_available--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vp.camera != nullptr)
|
|
|
|
{
|
|
|
|
ActorRenderFlags savedflags = vp.camera->renderflags;
|
2019-03-09 14:45:49 +00:00
|
|
|
di->CreateScene(drawmode == DM_MAINVIEW);
|
2019-03-01 18:01:06 +00:00
|
|
|
vp.camera->renderflags = savedflags;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-03-09 14:45:49 +00:00
|
|
|
di->CreateScene(false);
|
2019-03-01 18:01:06 +00:00
|
|
|
}
|
|
|
|
|
2019-03-04 14:55:43 +00:00
|
|
|
GetRenderState()->SetDepthMask(true);
|
2019-03-01 18:01:06 +00:00
|
|
|
if (!gl_no_skyclear) screen->mPortalState->RenderFirstSkyPortal(recursion, di, *GetRenderState());
|
|
|
|
|
|
|
|
di->RenderScene(*GetRenderState());
|
|
|
|
|
|
|
|
if (applySSAO && GetRenderState()->GetPassType() == GBUFFER_PASS)
|
|
|
|
{
|
2019-03-16 22:37:38 +00:00
|
|
|
mPostprocess->AmbientOccludeScene(di->VPUniforms.mProjectionMatrix.get()[5]);
|
2019-03-01 18:01:06 +00:00
|
|
|
screen->mViewpoints->Bind(*GetRenderState(), di->vpIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle all portals after rendering the opaque objects but before
|
|
|
|
// doing all translucent stuff
|
|
|
|
recursion++;
|
|
|
|
screen->mPortalState->EndFrame(di, *GetRenderState());
|
|
|
|
recursion--;
|
|
|
|
di->RenderTranslucent(*GetRenderState());
|
2019-02-20 20:21:57 +00:00
|
|
|
}
|
|
|
|
|
2019-03-07 17:05:12 +00:00
|
|
|
void VulkanFrameBuffer::PostProcessScene(int fixedcm, const std::function<void()> &afterBloomDrawEndScene2D)
|
|
|
|
{
|
|
|
|
mPostprocess->PostProcessScene(fixedcm, afterBloomDrawEndScene2D);
|
|
|
|
}
|
|
|
|
|
2019-02-20 20:21:57 +00:00
|
|
|
uint32_t VulkanFrameBuffer::GetCaps()
|
|
|
|
{
|
|
|
|
if (!V_IsHardwareRenderer())
|
|
|
|
return Super::GetCaps();
|
|
|
|
|
|
|
|
// describe our basic feature set
|
|
|
|
ActorRenderFeatureFlags FlagSet = RFF_FLATSPRITES | RFF_MODELS | RFF_SLOPE3DFLOORS |
|
|
|
|
RFF_TILTPITCH | RFF_ROLLSPRITES | RFF_POLYGONAL | RFF_MATSHADER | RFF_POSTSHADER | RFF_BRIGHTMAP;
|
|
|
|
if (r_drawvoxels)
|
|
|
|
FlagSet |= RFF_VOXELS;
|
|
|
|
|
|
|
|
if (gl_tonemap != 5) // not running palette tonemap shader
|
|
|
|
FlagSet |= RFF_TRUECOLOR;
|
|
|
|
|
|
|
|
return (uint32_t)FlagSet;
|
|
|
|
}
|
|
|
|
|
2019-08-11 19:16:09 +00:00
|
|
|
const char* VulkanFrameBuffer::DeviceName() const
|
|
|
|
{
|
|
|
|
const auto &props = device->PhysicalDevice.Properties;
|
|
|
|
return props.deviceName;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-02-20 20:21:57 +00:00
|
|
|
void VulkanFrameBuffer::SetVSync(bool vsync)
|
|
|
|
{
|
2019-04-11 02:26:43 +00:00
|
|
|
// This is handled in VulkanSwapChain::AcquireImage.
|
2019-10-01 18:50:15 +00:00
|
|
|
cur_vsync = vsync;
|
2019-02-20 20:21:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VulkanFrameBuffer::CleanForRestart()
|
|
|
|
{
|
2019-02-27 15:28:29 +00:00
|
|
|
// force recreation of the SW scene drawer to ensure it gets a new set of resources.
|
|
|
|
swdrawer.reset();
|
2019-02-20 20:21:57 +00:00
|
|
|
}
|
|
|
|
|
2019-03-17 21:41:02 +00:00
|
|
|
void VulkanFrameBuffer::PrecacheMaterial(FMaterial *mat, int translation)
|
|
|
|
{
|
2020-04-15 16:59:14 +00:00
|
|
|
if (mat->Source()->GetUseType() == ETextureType::SWCanvas) return;
|
2019-03-17 21:41:02 +00:00
|
|
|
|
2020-04-19 08:08:24 +00:00
|
|
|
MaterialLayerInfo* layer;
|
2019-03-17 21:41:02 +00:00
|
|
|
|
2020-04-13 15:05:49 +00:00
|
|
|
auto systex = static_cast<VkHardwareTexture*>(mat->GetLayer(0, translation, &layer));
|
2020-04-19 08:08:24 +00:00
|
|
|
systex->GetImage(layer->layerTexture, translation, layer->scaleFlags);
|
2020-04-13 15:05:49 +00:00
|
|
|
|
2020-04-19 08:08:24 +00:00
|
|
|
int numLayers = mat->NumLayers();
|
2020-04-13 15:05:49 +00:00
|
|
|
for (int i = 1; i < numLayers; i++)
|
|
|
|
{
|
|
|
|
auto systex = static_cast<VkHardwareTexture*>(mat->GetLayer(i, 0, &layer));
|
2020-04-19 08:08:24 +00:00
|
|
|
systex->GetImage(layer->layerTexture, 0, layer->scaleFlags);
|
2020-04-13 15:05:49 +00:00
|
|
|
}
|
2019-03-17 21:41:02 +00:00
|
|
|
}
|
|
|
|
|
2019-02-26 14:29:08 +00:00
|
|
|
IHardwareTexture *VulkanFrameBuffer::CreateHardwareTexture()
|
|
|
|
{
|
2019-03-01 17:31:33 +00:00
|
|
|
return new VkHardwareTexture();
|
2019-02-26 14:29:08 +00:00
|
|
|
}
|
|
|
|
|
2020-04-18 21:56:22 +00:00
|
|
|
FMaterial* VulkanFrameBuffer::CreateMaterial(FGameTexture* tex, int scaleflags)
|
|
|
|
{
|
|
|
|
return new VkMaterial(tex, scaleflags);
|
|
|
|
}
|
|
|
|
|
2019-02-20 20:21:57 +00:00
|
|
|
FModelRenderer *VulkanFrameBuffer::CreateModelRenderer(int mli)
|
|
|
|
{
|
2019-03-22 18:54:19 +00:00
|
|
|
return new FHWModelRenderer(nullptr, *GetRenderState(), mli);
|
2019-02-20 20:21:57 +00:00
|
|
|
}
|
|
|
|
|
2019-02-21 11:31:14 +00:00
|
|
|
IVertexBuffer *VulkanFrameBuffer::CreateVertexBuffer()
|
|
|
|
{
|
|
|
|
return new VKVertexBuffer();
|
|
|
|
}
|
|
|
|
|
|
|
|
IIndexBuffer *VulkanFrameBuffer::CreateIndexBuffer()
|
|
|
|
{
|
|
|
|
return new VKIndexBuffer();
|
|
|
|
}
|
|
|
|
|
2019-04-18 20:01:42 +00:00
|
|
|
IDataBuffer *VulkanFrameBuffer::CreateDataBuffer(int bindingpoint, bool ssbo, bool needsresize)
|
2019-02-21 11:31:14 +00:00
|
|
|
{
|
2019-04-18 20:01:42 +00:00
|
|
|
auto buffer = new VKDataBuffer(bindingpoint, ssbo, needsresize);
|
2019-03-15 06:54:34 +00:00
|
|
|
|
|
|
|
auto fb = GetVulkanFrameBuffer();
|
|
|
|
switch (bindingpoint)
|
2019-02-26 10:27:29 +00:00
|
|
|
{
|
2019-03-15 06:54:34 +00:00
|
|
|
case LIGHTBUF_BINDINGPOINT: LightBufferSSO = buffer; break;
|
|
|
|
case VIEWPOINT_BINDINGPOINT: ViewpointUBO = buffer; break;
|
|
|
|
case LIGHTNODES_BINDINGPOINT: LightNodes = buffer; break;
|
|
|
|
case LIGHTLINES_BINDINGPOINT: LightLines = buffer; break;
|
|
|
|
case LIGHTLIST_BINDINGPOINT: LightList = buffer; break;
|
|
|
|
case POSTPROCESS_BINDINGPOINT: break;
|
|
|
|
default: break;
|
2019-02-26 10:27:29 +00:00
|
|
|
}
|
2019-03-15 06:54:34 +00:00
|
|
|
|
2019-02-26 10:27:29 +00:00
|
|
|
return buffer;
|
2019-02-20 20:21:57 +00:00
|
|
|
}
|
|
|
|
|
2019-03-17 21:27:48 +00:00
|
|
|
void VulkanFrameBuffer::SetTextureFilterMode()
|
|
|
|
{
|
|
|
|
TextureFilterChanged();
|
|
|
|
}
|
|
|
|
|
2019-02-20 20:21:57 +00:00
|
|
|
void VulkanFrameBuffer::TextureFilterChanged()
|
|
|
|
{
|
2019-03-06 21:59:21 +00:00
|
|
|
if (mSamplerManager)
|
|
|
|
{
|
|
|
|
// Destroy the texture descriptors as they used the old samplers
|
2020-04-18 21:56:22 +00:00
|
|
|
VkMaterial::ResetAllDescriptors();
|
2019-03-06 21:59:21 +00:00
|
|
|
|
|
|
|
mSamplerManager->SetTextureFilterMode();
|
|
|
|
}
|
2019-02-20 20:21:57 +00:00
|
|
|
}
|
|
|
|
|
2019-03-21 20:57:39 +00:00
|
|
|
void VulkanFrameBuffer::StartPrecaching()
|
|
|
|
{
|
|
|
|
// Destroy the texture descriptors to avoid problems with potentially stale textures.
|
2020-04-18 21:56:22 +00:00
|
|
|
VkMaterial::ResetAllDescriptors();
|
2019-03-21 20:57:39 +00:00
|
|
|
}
|
|
|
|
|
2019-02-20 20:21:57 +00:00
|
|
|
void VulkanFrameBuffer::BlurScene(float amount)
|
|
|
|
{
|
2019-03-06 21:59:21 +00:00
|
|
|
if (mPostprocess)
|
|
|
|
mPostprocess->BlurScene(amount);
|
2019-02-20 20:21:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VulkanFrameBuffer::UpdatePalette()
|
|
|
|
{
|
2019-03-06 21:59:21 +00:00
|
|
|
if (mPostprocess)
|
|
|
|
mPostprocess->ClearTonemapPalette();
|
2019-02-20 20:21:57 +00:00
|
|
|
}
|
|
|
|
|
2019-03-13 01:23:33 +00:00
|
|
|
FTexture *VulkanFrameBuffer::WipeStartScreen()
|
|
|
|
{
|
2019-06-10 06:40:49 +00:00
|
|
|
SetViewportRects(nullptr);
|
|
|
|
|
|
|
|
auto tex = new FWrapperTexture(mScreenViewport.width, mScreenViewport.height, 1);
|
2019-03-13 01:23:33 +00:00
|
|
|
auto systex = static_cast<VkHardwareTexture*>(tex->GetSystemTexture());
|
|
|
|
|
2019-06-10 06:40:49 +00:00
|
|
|
systex->CreateWipeTexture(mScreenViewport.width, mScreenViewport.height, "WipeStartScreen");
|
2019-03-13 01:23:33 +00:00
|
|
|
|
|
|
|
return tex;
|
|
|
|
}
|
|
|
|
|
|
|
|
FTexture *VulkanFrameBuffer::WipeEndScreen()
|
|
|
|
{
|
|
|
|
GetPostprocess()->SetActiveRenderTarget();
|
|
|
|
Draw2D();
|
|
|
|
Clear2D();
|
|
|
|
|
2019-06-10 06:40:49 +00:00
|
|
|
auto tex = new FWrapperTexture(mScreenViewport.width, mScreenViewport.height, 1);
|
2019-03-13 01:23:33 +00:00
|
|
|
auto systex = static_cast<VkHardwareTexture*>(tex->GetSystemTexture());
|
|
|
|
|
2019-06-10 06:40:49 +00:00
|
|
|
systex->CreateWipeTexture(mScreenViewport.width, mScreenViewport.height, "WipeEndScreen");
|
2019-03-13 01:23:33 +00:00
|
|
|
|
|
|
|
return tex;
|
|
|
|
}
|
|
|
|
|
2019-03-14 22:07:52 +00:00
|
|
|
void VulkanFrameBuffer::CopyScreenToBuffer(int w, int h, void *data)
|
2019-03-13 13:10:13 +00:00
|
|
|
{
|
2019-05-16 16:26:47 +00:00
|
|
|
VkTextureImage image;
|
|
|
|
|
2019-03-13 13:10:13 +00:00
|
|
|
// Convert from rgba16f to rgba8 using the GPU:
|
|
|
|
ImageBuilder imgbuilder;
|
|
|
|
imgbuilder.setFormat(VK_FORMAT_R8G8B8A8_UNORM);
|
|
|
|
imgbuilder.setUsage(VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
|
|
|
|
imgbuilder.setSize(w, h);
|
2019-05-16 16:26:47 +00:00
|
|
|
image.Image = imgbuilder.create(device);
|
|
|
|
GetPostprocess()->BlitCurrentToImage(&image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
2019-03-13 13:10:13 +00:00
|
|
|
|
|
|
|
// Staging buffer for download
|
|
|
|
BufferBuilder bufbuilder;
|
|
|
|
bufbuilder.setSize(w * h * 4);
|
|
|
|
bufbuilder.setUsage(VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_TO_CPU);
|
|
|
|
auto staging = bufbuilder.create(device);
|
|
|
|
|
|
|
|
// Copy from image to buffer
|
|
|
|
VkBufferImageCopy region = {};
|
|
|
|
region.imageExtent.width = w;
|
|
|
|
region.imageExtent.height = h;
|
|
|
|
region.imageExtent.depth = 1;
|
|
|
|
region.imageSubresource.layerCount = 1;
|
|
|
|
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
2019-05-16 16:26:47 +00:00
|
|
|
GetDrawCommands()->copyImageToBuffer(image.Image->image, image.Layout, staging->buffer, 1, ®ion);
|
2019-03-13 13:10:13 +00:00
|
|
|
|
|
|
|
// Submit command buffers and wait for device to finish the work
|
2019-04-19 18:55:15 +00:00
|
|
|
WaitForCommands(false);
|
2019-03-13 13:10:13 +00:00
|
|
|
|
|
|
|
// Map and convert from rgba8 to rgb8
|
2019-03-14 22:07:52 +00:00
|
|
|
uint8_t *dest = (uint8_t*)data;
|
2019-03-13 13:10:13 +00:00
|
|
|
uint8_t *pixels = (uint8_t*)staging->Map(0, w * h * 4);
|
|
|
|
int dindex = 0;
|
|
|
|
for (int y = 0; y < h; y++)
|
|
|
|
{
|
|
|
|
int sindex = (h - y - 1) * w * 4;
|
|
|
|
for (int x = 0; x < w; x++)
|
|
|
|
{
|
2019-03-14 22:07:52 +00:00
|
|
|
dest[dindex] = pixels[sindex];
|
|
|
|
dest[dindex + 1] = pixels[sindex + 1];
|
|
|
|
dest[dindex + 2] = pixels[sindex + 2];
|
2019-03-13 13:10:13 +00:00
|
|
|
dindex += 3;
|
|
|
|
sindex += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
staging->Unmap();
|
2019-03-14 22:07:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TArray<uint8_t> VulkanFrameBuffer::GetScreenshotBuffer(int &pitch, ESSType &color_type, float &gamma)
|
|
|
|
{
|
|
|
|
int w = SCREENWIDTH;
|
|
|
|
int h = SCREENHEIGHT;
|
|
|
|
|
2019-05-20 10:36:45 +00:00
|
|
|
IntRect box;
|
|
|
|
box.left = 0;
|
|
|
|
box.top = 0;
|
|
|
|
box.width = w;
|
|
|
|
box.height = h;
|
|
|
|
mPostprocess->DrawPresentTexture(box, true, true);
|
|
|
|
|
2019-03-14 22:07:52 +00:00
|
|
|
TArray<uint8_t> ScreenshotBuffer(w * h * 3, true);
|
|
|
|
CopyScreenToBuffer(w, h, ScreenshotBuffer.Data());
|
2019-03-13 13:10:13 +00:00
|
|
|
|
|
|
|
pitch = w * 3;
|
|
|
|
color_type = SS_RGB;
|
2019-05-10 16:02:27 +00:00
|
|
|
gamma = 1.0f;
|
2019-03-13 13:10:13 +00:00
|
|
|
return ScreenshotBuffer;
|
|
|
|
}
|
|
|
|
|
2019-02-20 20:21:57 +00:00
|
|
|
void VulkanFrameBuffer::BeginFrame()
|
|
|
|
{
|
2019-03-07 11:24:56 +00:00
|
|
|
SetViewportRects(nullptr);
|
2019-03-05 03:59:17 +00:00
|
|
|
mScreenBuffers->BeginFrame(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height);
|
|
|
|
mSaveBuffers->BeginFrame(SAVEPICWIDTH, SAVEPICHEIGHT, SAVEPICWIDTH, SAVEPICHEIGHT);
|
2019-03-17 20:14:51 +00:00
|
|
|
mRenderState->BeginFrame();
|
2019-03-15 06:54:34 +00:00
|
|
|
mRenderPassManager->UpdateDynamicSet();
|
2019-05-10 00:16:26 +00:00
|
|
|
|
|
|
|
if (mNextTimestampQuery > 0)
|
|
|
|
{
|
|
|
|
GetDrawCommands()->resetQueryPool(mTimestampQueryPool.get(), 0, mNextTimestampQuery);
|
|
|
|
mNextTimestampQuery = 0;
|
|
|
|
}
|
2019-02-20 20:21:57 +00:00
|
|
|
}
|
|
|
|
|
2019-04-30 20:55:35 +00:00
|
|
|
void VulkanFrameBuffer::PushGroup(const FString &name)
|
|
|
|
{
|
|
|
|
if (!gpuStatActive)
|
|
|
|
return;
|
|
|
|
|
2019-07-03 14:49:06 +00:00
|
|
|
if (mNextTimestampQuery < VulkanFrameBuffer::MaxTimestampQueries && device->graphicsTimeQueries)
|
2019-04-30 20:55:35 +00:00
|
|
|
{
|
|
|
|
TimestampQuery q;
|
|
|
|
q.name = name;
|
|
|
|
q.startIndex = mNextTimestampQuery++;
|
|
|
|
q.endIndex = 0;
|
|
|
|
GetDrawCommands()->writeTimestamp(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, mTimestampQueryPool.get(), q.startIndex);
|
|
|
|
mGroupStack.push_back(timeElapsedQueries.size());
|
|
|
|
timeElapsedQueries.push_back(q);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VulkanFrameBuffer::PopGroup()
|
|
|
|
{
|
|
|
|
if (!gpuStatActive || mGroupStack.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
TimestampQuery &q = timeElapsedQueries[mGroupStack.back()];
|
|
|
|
mGroupStack.pop_back();
|
|
|
|
|
2019-07-03 14:49:06 +00:00
|
|
|
if (mNextTimestampQuery < VulkanFrameBuffer::MaxTimestampQueries && device->graphicsTimeQueries)
|
2019-04-30 20:55:35 +00:00
|
|
|
{
|
|
|
|
q.endIndex = mNextTimestampQuery++;
|
|
|
|
GetDrawCommands()->writeTimestamp(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, mTimestampQueryPool.get(), q.endIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VulkanFrameBuffer::UpdateGpuStats()
|
|
|
|
{
|
|
|
|
uint64_t timestamps[MaxTimestampQueries];
|
2019-05-03 14:10:14 +00:00
|
|
|
if (mNextTimestampQuery > 0)
|
|
|
|
mTimestampQueryPool->getResults(0, mNextTimestampQuery, sizeof(uint64_t) * mNextTimestampQuery, timestamps, sizeof(uint64_t), VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
|
2019-04-30 20:55:35 +00:00
|
|
|
|
|
|
|
double timestampPeriod = device->PhysicalDevice.Properties.limits.timestampPeriod;
|
|
|
|
|
|
|
|
gpuStatOutput = "";
|
|
|
|
for (auto &q : timeElapsedQueries)
|
|
|
|
{
|
|
|
|
if (q.endIndex <= q.startIndex)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
int64_t timeElapsed = std::max(static_cast<int64_t>(timestamps[q.endIndex] - timestamps[q.startIndex]), (int64_t)0);
|
|
|
|
double timeNS = timeElapsed * timestampPeriod;
|
|
|
|
|
|
|
|
FString out;
|
|
|
|
out.Format("%s=%04.2f ms\n", q.name.GetChars(), timeNS / 1000000.0f);
|
|
|
|
gpuStatOutput += out;
|
|
|
|
}
|
|
|
|
timeElapsedQueries.clear();
|
|
|
|
mGroupStack.clear();
|
|
|
|
|
|
|
|
gpuStatActive = keepGpuStatActive;
|
|
|
|
keepGpuStatActive = false;
|
|
|
|
}
|
|
|
|
|
2019-02-20 20:21:57 +00:00
|
|
|
void VulkanFrameBuffer::Draw2D()
|
|
|
|
{
|
2019-02-21 21:49:00 +00:00
|
|
|
::Draw2D(&m2DDrawer, *mRenderState);
|
2019-02-20 20:21:57 +00:00
|
|
|
}
|
2019-02-21 11:31:14 +00:00
|
|
|
|
2019-03-24 17:03:10 +00:00
|
|
|
VulkanCommandBuffer *VulkanFrameBuffer::GetTransferCommands()
|
|
|
|
{
|
|
|
|
if (!mTransferCommands)
|
|
|
|
{
|
2019-04-04 13:58:48 +00:00
|
|
|
mTransferCommands = mCommandPool->createBuffer();
|
2019-03-24 17:03:10 +00:00
|
|
|
mTransferCommands->SetDebugName("VulkanFrameBuffer.mTransferCommands");
|
|
|
|
mTransferCommands->begin();
|
|
|
|
}
|
|
|
|
return mTransferCommands.get();
|
|
|
|
}
|
|
|
|
|
2019-02-21 11:31:14 +00:00
|
|
|
VulkanCommandBuffer *VulkanFrameBuffer::GetDrawCommands()
|
|
|
|
{
|
2019-02-27 14:37:37 +00:00
|
|
|
if (!mDrawCommands)
|
|
|
|
{
|
2019-04-04 13:58:48 +00:00
|
|
|
mDrawCommands = mCommandPool->createBuffer();
|
2019-03-09 20:34:29 +00:00
|
|
|
mDrawCommands->SetDebugName("VulkanFrameBuffer.mDrawCommands");
|
2019-02-27 14:37:37 +00:00
|
|
|
mDrawCommands->begin();
|
|
|
|
}
|
|
|
|
return mDrawCommands.get();
|
2019-02-21 11:31:14 +00:00
|
|
|
}
|
2019-03-03 13:32:03 +00:00
|
|
|
|
2019-03-05 01:06:20 +00:00
|
|
|
unsigned int VulkanFrameBuffer::GetLightBufferBlockSize() const
|
|
|
|
{
|
|
|
|
return mLights->GetBlockSize();
|
|
|
|
}
|
|
|
|
|
2019-03-03 13:32:03 +00:00
|
|
|
void VulkanFrameBuffer::PrintStartupLog()
|
|
|
|
{
|
2019-08-11 19:16:09 +00:00
|
|
|
const auto &props = device->PhysicalDevice.Properties;
|
2019-03-09 09:20:14 +00:00
|
|
|
|
2019-03-03 13:32:03 +00:00
|
|
|
FString deviceType;
|
2019-03-09 09:20:14 +00:00
|
|
|
switch (props.deviceType)
|
2019-03-03 13:32:03 +00:00
|
|
|
{
|
|
|
|
case VK_PHYSICAL_DEVICE_TYPE_OTHER: deviceType = "other"; break;
|
|
|
|
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: deviceType = "integrated gpu"; break;
|
|
|
|
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: deviceType = "discrete gpu"; break;
|
|
|
|
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: deviceType = "virtual gpu"; break;
|
|
|
|
case VK_PHYSICAL_DEVICE_TYPE_CPU: deviceType = "cpu"; break;
|
2019-03-09 09:20:14 +00:00
|
|
|
default: deviceType.Format("%d", (int)props.deviceType); break;
|
2019-03-03 13:32:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FString apiVersion, driverVersion;
|
2019-03-09 09:20:14 +00:00
|
|
|
apiVersion.Format("%d.%d.%d", VK_VERSION_MAJOR(props.apiVersion), VK_VERSION_MINOR(props.apiVersion), VK_VERSION_PATCH(props.apiVersion));
|
|
|
|
driverVersion.Format("%d.%d.%d", VK_VERSION_MAJOR(props.driverVersion), VK_VERSION_MINOR(props.driverVersion), VK_VERSION_PATCH(props.driverVersion));
|
2019-03-03 13:32:03 +00:00
|
|
|
|
2019-03-09 09:20:14 +00:00
|
|
|
Printf("Vulkan device: " TEXTCOLOR_ORANGE "%s\n", props.deviceName);
|
2019-03-03 13:32:03 +00:00
|
|
|
Printf("Vulkan device type: %s\n", deviceType.GetChars());
|
|
|
|
Printf("Vulkan version: %s (api) %s (driver)\n", apiVersion.GetChars(), driverVersion.GetChars());
|
|
|
|
|
|
|
|
Printf(PRINT_LOG, "Vulkan extensions:");
|
2019-03-09 09:20:14 +00:00
|
|
|
for (const VkExtensionProperties &p : device->PhysicalDevice.Extensions)
|
2019-03-03 13:32:03 +00:00
|
|
|
{
|
|
|
|
Printf(PRINT_LOG, " %s", p.extensionName);
|
|
|
|
}
|
|
|
|
Printf(PRINT_LOG, "\n");
|
|
|
|
|
2019-03-09 09:20:14 +00:00
|
|
|
const auto &limits = props.limits;
|
2019-03-03 13:32:03 +00:00
|
|
|
Printf("Max. texture size: %d\n", limits.maxImageDimension2D);
|
|
|
|
Printf("Max. uniform buffer range: %d\n", limits.maxUniformBufferRange);
|
2019-03-30 09:15:49 +00:00
|
|
|
Printf("Min. uniform buffer offset alignment: %llu\n", limits.minUniformBufferOffsetAlignment);
|
2019-03-03 13:32:03 +00:00
|
|
|
}
|
2019-03-08 20:34:21 +00:00
|
|
|
|
|
|
|
void VulkanFrameBuffer::CreateFanToTrisIndexBuffer()
|
|
|
|
{
|
|
|
|
TArray<uint32_t> data;
|
|
|
|
for (int i = 2; i < 1000; i++)
|
|
|
|
{
|
|
|
|
data.Push(0);
|
|
|
|
data.Push(i - 1);
|
|
|
|
data.Push(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
FanToTrisIndexBuffer.reset(CreateIndexBuffer());
|
|
|
|
FanToTrisIndexBuffer->SetData(sizeof(uint32_t) * data.Size(), data.Data());
|
|
|
|
}
|
2019-03-15 06:54:34 +00:00
|
|
|
|
|
|
|
void VulkanFrameBuffer::UpdateShadowMap()
|
|
|
|
{
|
|
|
|
mPostprocess->UpdateShadowMap();
|
|
|
|
}
|