raze/source/common/rendering/vulkan/system/vk_framebuffer.cpp
Christoph Oelckers 84173ee09b - backend update from GZDoom.
The main bulk of this is the new start screen code. To make this work in Raze some more work on the startup procedure is needed.
What this does provide is support for the DOS end-of-game text screens in Duke and SW on non-Windows systems.
2022-06-06 11:45:34 +02:00

834 lines
24 KiB
C++

/*
** 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.
**
*/
#include "volk/volk.h"
#include <inttypes.h>
#include "v_video.h"
#include "m_png.h"
#include "r_videoscale.h"
#include "i_time.h"
#include "v_text.h"
#include "version.h"
#include "v_draw.h"
#include "hw_clock.h"
#include "hw_vrmodes.h"
#include "hw_cvars.h"
#include "hw_skydome.h"
#include "hwrenderer/data/hw_viewpointbuffer.h"
#include "flatvertices.h"
#include "hwrenderer/data/shaderuniforms.h"
#include "hw_lightbuffer.h"
#include "vk_framebuffer.h"
#include "vk_buffers.h"
#include "vulkan/renderer/vk_renderstate.h"
#include "vulkan/renderer/vk_renderpass.h"
#include "vulkan/renderer/vk_streambuffer.h"
#include "vulkan/renderer/vk_postprocess.h"
#include "vulkan/renderer/vk_renderbuffers.h"
#include "vulkan/shaders/vk_shader.h"
#include "vulkan/textures/vk_samplers.h"
#include "vulkan/textures/vk_hwtexture.h"
#include "vulkan/system/vk_builders.h"
#include "vulkan/system/vk_swapchain.h"
#include "engineerrors.h"
#include "c_dispatch.h"
EXTERN_CVAR(Bool, r_drawvoxels)
EXTERN_CVAR(Int, gl_tonemap)
EXTERN_CVAR(Int, screenblocks)
EXTERN_CVAR(Bool, cl_capfps)
extern int rendered_commandbuffers;
int current_rendered_commandbuffers;
extern bool gpuStatActive;
extern bool keepGpuStatActive;
extern FString gpuStatOutput;
CCMD(vk_memstats)
{
VmaStats stats = {};
vmaCalculateStats(GetVulkanFrameBuffer()->device->allocator, &stats);
Printf("Allocated objects: %d, used bytes: %d MB\n", (int)stats.total.allocationCount, (int)stats.total.usedBytes / (1024 * 1024));
Printf("Unused range count: %d, unused bytes: %d MB\n", (int)stats.total.unusedRangeCount, (int)stats.total.unusedBytes / (1024 * 1024));
}
VulkanFrameBuffer::VulkanFrameBuffer(void *hMonitor, bool fullscreen, VulkanDevice *dev) :
Super(hMonitor, fullscreen)
{
device = dev;
swapChain = std::make_unique<VulkanSwapChain>(device);
mSwapChainImageAvailableSemaphore.reset(new VulkanSemaphore(device));
mRenderFinishedSemaphore.reset(new VulkanSemaphore(device));
for (auto &semaphore : mSubmitSemaphore)
semaphore.reset(new VulkanSemaphore(device));
for (auto &fence : mSubmitFence)
fence.reset(new VulkanFence(device));
for (int i = 0; i < maxConcurrentSubmitCount; i++)
mSubmitWaitFences[i] = mSubmitFence[i]->fence;
}
VulkanFrameBuffer::~VulkanFrameBuffer()
{
vkDeviceWaitIdle(device->device); // make sure the GPU is no longer using any objects before RAII tears them down
// 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;
// All descriptors must be destroyed before the descriptor pool in renderpass manager is destroyed
VkHardwareTexture::ResetAll();
VKBuffer::ResetAll();
PPResource::ResetAll();
delete MatrixBuffer;
delete StreamBuffer;
delete mVertexData;
delete mSkyData;
delete mViewpoints;
delete mLights;
mShadowMap.Reset();
screen = tmp;
DeleteFrameObjects();
}
void VulkanFrameBuffer::InitializeState()
{
static bool first = true;
if (first)
{
PrintStartupLog();
first = false;
}
// Use the same names here as OpenGL returns.
switch (device->PhysicalDevice.Properties.vendorID)
{
case 0x1002: vendorstring = "ATI Technologies Inc."; break;
case 0x10DE: vendorstring = "NVIDIA Corporation"; break;
case 0x8086: vendorstring = "Intel"; break;
default: vendorstring = "Unknown"; break;
}
hwcaps = RFL_SHADER_STORAGE_BUFFER | RFL_BUFFER_STORAGE;
glslversion = 4.50f;
uniformblockalignment = (unsigned int)device->PhysicalDevice.Properties.limits.minUniformBufferOffsetAlignment;
maxuniformblock = device->PhysicalDevice.Properties.limits.maxUniformBufferRange;
mCommandPool.reset(new VulkanCommandPool(device, device->graphicsFamily));
mScreenBuffers.reset(new VkRenderBuffers());
mSaveBuffers.reset(new VkRenderBuffers());
mActiveRenderBuffers = mScreenBuffers.get();
mPostprocess.reset(new VkPostprocess());
mRenderPassManager.reset(new VkRenderPassManager());
mVertexData = new FFlatVertexBuffer(GetWidth(), GetHeight());
mSkyData = new FSkyVertexBuffer;
mViewpoints = new HWViewpointBuffer;
mLights = new FLightBuffer();
CreateFanToTrisIndexBuffer();
// To do: move this to HW renderer interface maybe?
MatrixBuffer = new VkStreamBuffer(sizeof(MatricesUBO), 50000);
StreamBuffer = new VkStreamBuffer(sizeof(StreamUBO), 300);
mShaderManager.reset(new VkShaderManager(device));
mSamplerManager.reset(new VkSamplerManager(device));
mRenderPassManager->Init();
#ifdef __APPLE__
mRenderState.reset(new VkRenderStateMolten());
#else
mRenderState.reset(new VkRenderState());
#endif
if (device->graphicsTimeQueries)
{
QueryPoolBuilder querybuilder;
querybuilder.setQueryType(VK_QUERY_TYPE_TIMESTAMP, MaxTimestampQueries);
mTimestampQueryPool = querybuilder.create(device);
GetDrawCommands()->resetQueryPool(mTimestampQueryPool.get(), 0, MaxTimestampQueries);
}
}
void VulkanFrameBuffer::Update()
{
twoD.Reset();
Flush3D.Reset();
Flush3D.Clock();
GetPostprocess()->SetActiveRenderTarget();
Draw2D();
twod->Clear();
mRenderState->EndRenderPass();
mRenderState->EndFrame();
Flush3D.Unclock();
WaitForCommands(true);
UpdateGpuStats();
Super::Update();
}
bool VulkanFrameBuffer::CompileNextShader()
{
return mShaderManager->CompileNextShader();
}
void VulkanFrameBuffer::DeleteFrameObjects(bool uploadOnly)
{
FrameTextureUpload.Buffers.clear();
FrameTextureUpload.TotalSize = 0;
if (!uploadOnly)
{
FrameDeleteList.Images.clear();
FrameDeleteList.ImageViews.clear();
FrameDeleteList.Framebuffers.clear();
FrameDeleteList.Buffers.clear();
FrameDeleteList.Descriptors.clear();
FrameDeleteList.DescriptorPools.clear();
FrameDeleteList.CommandBuffers.clear();
}
}
void VulkanFrameBuffer::FlushCommands(VulkanCommandBuffer **commands, size_t count, bool finish, bool lastsubmit)
{
int currentIndex = mNextSubmit % maxConcurrentSubmitCount;
if (mNextSubmit >= maxConcurrentSubmitCount)
{
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]);
if (mNextSubmit > 0)
submit.addWait(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, mSubmitSemaphore[(mNextSubmit - 1) % maxConcurrentSubmitCount].get());
if (finish && presentImageIndex != 0xffffffff)
{
submit.addWait(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, mSwapChainImageAvailableSemaphore.get());
submit.addSignal(mRenderFinishedSemaphore.get());
}
if (!lastsubmit)
submit.addSignal(mSubmitSemaphore[currentIndex].get());
submit.execute(device, device->graphicsQueue, mSubmitFence[currentIndex].get());
mNextSubmit++;
}
void VulkanFrameBuffer::FlushCommands(bool finish, bool lastsubmit, bool uploadOnly)
{
if (!uploadOnly)
mRenderState->EndRenderPass();
if ((!uploadOnly && mDrawCommands) || mTransferCommands)
{
VulkanCommandBuffer *commands[2];
size_t count = 0;
if (mTransferCommands)
{
mTransferCommands->end();
commands[count++] = mTransferCommands.get();
FrameDeleteList.CommandBuffers.push_back(std::move(mTransferCommands));
}
if (!uploadOnly && mDrawCommands)
{
mDrawCommands->end();
commands[count++] = mDrawCommands.get();
FrameDeleteList.CommandBuffers.push_back(std::move(mDrawCommands));
}
FlushCommands(commands, count, finish, lastsubmit);
current_rendered_commandbuffers += (int)count;
}
}
void VulkanFrameBuffer::WaitForCommands(bool finish, bool uploadOnly)
{
if (finish)
{
Finish.Reset();
Finish.Clock();
presentImageIndex = swapChain->AcquireImage(GetClientWidth(), GetClientHeight(), mSwapChainImageAvailableSemaphore.get());
if (presentImageIndex != 0xffffffff)
mPostprocess->DrawPresentTexture(mOutputLetterbox, true, false);
}
FlushCommands(finish, true, uploadOnly);
if (finish)
{
FPSLimit();
if (presentImageIndex != 0xffffffff)
swapChain->QueuePresent(presentImageIndex, mRenderFinishedSemaphore.get());
}
int numWaitFences = min(mNextSubmit, (int)maxConcurrentSubmitCount);
if (numWaitFences > 0)
{
vkWaitForFences(device->device, numWaitFences, mSubmitWaitFences, VK_TRUE, std::numeric_limits<uint64_t>::max());
vkResetFences(device->device, numWaitFences, mSubmitWaitFences);
}
DeleteFrameObjects(uploadOnly);
mNextSubmit = 0;
if (finish)
{
Finish.Unclock();
rendered_commandbuffers = current_rendered_commandbuffers;
current_rendered_commandbuffers = 0;
}
}
void VulkanFrameBuffer::RenderTextureView(FCanvasTexture* tex, std::function<void(IntRect &)> renderFunc)
{
auto BaseLayer = static_cast<VkHardwareTexture*>(tex->GetHardwareTexture(0, 0));
VkTextureImage *image = BaseLayer->GetImage(tex, 0, 0);
VkTextureImage *depthStencil = BaseLayer->GetDepthStencil(tex);
mRenderState->EndRenderPass();
VkImageTransition barrier0;
barrier0.addImage(image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, true);
barrier0.execute(GetDrawCommands());
mRenderState->SetRenderTarget(image, depthStencil->View.get(), image->Image->width, image->Image->height, VK_FORMAT_R8G8B8A8_UNORM, VK_SAMPLE_COUNT_1_BIT);
IntRect bounds;
bounds.left = bounds.top = 0;
bounds.width = min(tex->GetWidth(), image->Image->width);
bounds.height = min(tex->GetHeight(), image->Image->height);
renderFunc(bounds);
mRenderState->EndRenderPass();
VkImageTransition barrier1;
barrier1.addImage(image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, false);
barrier1.execute(GetDrawCommands());
mRenderState->SetRenderTarget(&GetBuffers()->SceneColor, GetBuffers()->SceneDepthStencil.View.get(), GetBuffers()->GetWidth(), GetBuffers()->GetHeight(), VK_FORMAT_R16G16B16A16_SFLOAT, GetBuffers()->GetSceneSamples());
tex->SetUpdated(true);
}
void VulkanFrameBuffer::PostProcessScene(bool swscene, int fixedcm, float flash, const std::function<void()> &afterBloomDrawEndScene2D)
{
if (!swscene) mPostprocess->BlitSceneToPostprocess(); // Copy the resulting scene to the current post process texture
mPostprocess->PostProcessScene(fixedcm, flash, afterBloomDrawEndScene2D);
}
const char* VulkanFrameBuffer::DeviceName() const
{
const auto &props = device->PhysicalDevice.Properties;
return props.deviceName;
}
void VulkanFrameBuffer::SetVSync(bool vsync)
{
// This is handled in VulkanSwapChain::AcquireImage.
cur_vsync = vsync;
}
void VulkanFrameBuffer::PrecacheMaterial(FMaterial *mat, int translation)
{
if (mat->Source()->GetUseType() == ETextureType::SWCanvas) return;
MaterialLayerInfo* layer;
auto systex = static_cast<VkHardwareTexture*>(mat->GetLayer(0, translation, &layer));
systex->GetImage(layer->layerTexture, translation, layer->scaleFlags);
int numLayers = mat->NumLayers();
for (int i = 1; i < numLayers; i++)
{
auto syslayer = static_cast<VkHardwareTexture*>(mat->GetLayer(i, 0, &layer));
syslayer->GetImage(layer->layerTexture, 0, layer->scaleFlags);
}
}
IHardwareTexture *VulkanFrameBuffer::CreateHardwareTexture(int numchannels)
{
return new VkHardwareTexture(numchannels);
}
FMaterial* VulkanFrameBuffer::CreateMaterial(FGameTexture* tex, int scaleflags)
{
return new VkMaterial(tex, scaleflags);
}
IVertexBuffer *VulkanFrameBuffer::CreateVertexBuffer()
{
return new VKVertexBuffer();
}
IIndexBuffer *VulkanFrameBuffer::CreateIndexBuffer()
{
return new VKIndexBuffer();
}
IDataBuffer *VulkanFrameBuffer::CreateDataBuffer(int bindingpoint, bool ssbo, bool needsresize)
{
auto buffer = new VKDataBuffer(bindingpoint, ssbo, needsresize);
switch (bindingpoint)
{
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;
}
return buffer;
}
void VulkanFrameBuffer::SetTextureFilterMode()
{
if (mSamplerManager)
{
// Destroy the texture descriptors as they used the old samplers
VkMaterial::ResetAllDescriptors();
mSamplerManager->SetTextureFilterMode();
}
}
void VulkanFrameBuffer::StartPrecaching()
{
// Destroy the texture descriptors to avoid problems with potentially stale textures.
VkMaterial::ResetAllDescriptors();
}
void VulkanFrameBuffer::BlurScene(float amount)
{
if (mPostprocess)
mPostprocess->BlurScene(amount);
}
void VulkanFrameBuffer::UpdatePalette()
{
if (mPostprocess)
mPostprocess->ClearTonemapPalette();
}
FTexture *VulkanFrameBuffer::WipeStartScreen()
{
SetViewportRects(nullptr);
auto tex = new FWrapperTexture(mScreenViewport.width, mScreenViewport.height, 1);
auto systex = static_cast<VkHardwareTexture*>(tex->GetSystemTexture());
systex->CreateWipeTexture(mScreenViewport.width, mScreenViewport.height, "WipeStartScreen");
return tex;
}
FTexture *VulkanFrameBuffer::WipeEndScreen()
{
GetPostprocess()->SetActiveRenderTarget();
Draw2D();
twod->Clear();
auto tex = new FWrapperTexture(mScreenViewport.width, mScreenViewport.height, 1);
auto systex = static_cast<VkHardwareTexture*>(tex->GetSystemTexture());
systex->CreateWipeTexture(mScreenViewport.width, mScreenViewport.height, "WipeEndScreen");
return tex;
}
void VulkanFrameBuffer::CopyScreenToBuffer(int w, int h, uint8_t *data)
{
VkTextureImage image;
// 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);
image.Image = imgbuilder.create(device);
GetPostprocess()->BlitCurrentToImage(&image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
// 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;
GetDrawCommands()->copyImageToBuffer(image.Image->image, image.Layout, staging->buffer, 1, &region);
// Submit command buffers and wait for device to finish the work
WaitForCommands(false);
// Map and convert from rgba8 to rgb8
uint8_t *dest = (uint8_t*)data;
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++)
{
dest[dindex] = pixels[sindex];
dest[dindex + 1] = pixels[sindex + 1];
dest[dindex + 2] = pixels[sindex + 2];
dindex += 3;
sindex += 4;
}
}
staging->Unmap();
}
void VulkanFrameBuffer::SetActiveRenderTarget()
{
mPostprocess->SetActiveRenderTarget();
}
TArray<uint8_t> VulkanFrameBuffer::GetScreenshotBuffer(int &pitch, ESSType &color_type, float &gamma)
{
int w = SCREENWIDTH;
int h = SCREENHEIGHT;
IntRect box;
box.left = 0;
box.top = 0;
box.width = w;
box.height = h;
mPostprocess->DrawPresentTexture(box, true, true);
TArray<uint8_t> ScreenshotBuffer(w * h * 3, true);
CopyScreenToBuffer(w, h, ScreenshotBuffer.Data());
pitch = w * 3;
color_type = SS_RGB;
gamma = 1.0f;
return ScreenshotBuffer;
}
void VulkanFrameBuffer::BeginFrame()
{
SetViewportRects(nullptr);
mScreenBuffers->BeginFrame(screen->mScreenViewport.width, screen->mScreenViewport.height, screen->mSceneViewport.width, screen->mSceneViewport.height);
mSaveBuffers->BeginFrame(SAVEPICWIDTH, SAVEPICHEIGHT, SAVEPICWIDTH, SAVEPICHEIGHT);
mRenderState->BeginFrame();
mRenderPassManager->UpdateDynamicSet();
if (mNextTimestampQuery > 0)
{
GetDrawCommands()->resetQueryPool(mTimestampQueryPool.get(), 0, mNextTimestampQuery);
mNextTimestampQuery = 0;
}
}
void VulkanFrameBuffer::InitLightmap(int LMTextureSize, int LMTextureCount, TArray<uint16_t>& LMTextureData)
{
if (LMTextureData.Size() > 0)
{
int w = LMTextureSize;
int h = LMTextureSize;
int count = LMTextureCount;
int pixelsize = 8;
auto& lightmap = mActiveRenderBuffers->Lightmap;
if (lightmap.Image)
{
FrameDeleteList.Images.push_back(std::move(lightmap.Image));
FrameDeleteList.ImageViews.push_back(std::move(lightmap.View));
lightmap.reset();
}
ImageBuilder builder;
builder.setSize(w, h, 1, count);
builder.setFormat(VK_FORMAT_R16G16B16A16_SFLOAT);
builder.setUsage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT);
lightmap.Image = builder.create(device);
lightmap.Image->SetDebugName("VkRenderBuffers.Lightmap");
ImageViewBuilder viewbuilder;
viewbuilder.setType(VK_IMAGE_VIEW_TYPE_2D_ARRAY);
viewbuilder.setImage(lightmap.Image.get(), VK_FORMAT_R16G16B16A16_SFLOAT);
lightmap.View = viewbuilder.create(device);
lightmap.View->SetDebugName("VkRenderBuffers.LightmapView");
auto cmdbuffer = GetTransferCommands();
int totalSize = w * h * count * pixelsize;
BufferBuilder bufbuilder;
bufbuilder.setSize(totalSize);
bufbuilder.setUsage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY);
std::unique_ptr<VulkanBuffer> stagingBuffer = bufbuilder.create(device);
stagingBuffer->SetDebugName("VkHardwareTexture.mStagingBuffer");
uint16_t one = 0x3c00; // half-float 1.0
uint16_t* src = &LMTextureData[0];
uint16_t* data = (uint16_t*)stagingBuffer->Map(0, totalSize);
for (int i = w * h * count; i > 0; i--)
{
*(data++) = *(src++);
*(data++) = *(src++);
*(data++) = *(src++);
*(data++) = one;
}
stagingBuffer->Unmap();
VkImageTransition imageTransition;
imageTransition.addImage(&lightmap, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, true, 0, count);
imageTransition.execute(cmdbuffer);
VkBufferImageCopy region = {};
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.layerCount = count;
region.imageExtent.depth = 1;
region.imageExtent.width = w;
region.imageExtent.height = h;
cmdbuffer->copyBufferToImage(stagingBuffer->buffer, lightmap.Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
VkImageTransition barrier;
barrier.addImage(&lightmap, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, false, 0, count);
barrier.execute(cmdbuffer);
FrameTextureUpload.Buffers.push_back(std::move(stagingBuffer));
FrameTextureUpload.TotalSize += totalSize;
LMTextureData.Reset(); // We no longer need this, release the memory
}
}
void VulkanFrameBuffer::PushGroup(const FString &name)
{
if (!gpuStatActive)
return;
if (mNextTimestampQuery < VulkanFrameBuffer::MaxTimestampQueries && device->graphicsTimeQueries)
{
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();
if (mNextTimestampQuery < VulkanFrameBuffer::MaxTimestampQueries && device->graphicsTimeQueries)
{
q.endIndex = mNextTimestampQuery++;
GetDrawCommands()->writeTimestamp(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, mTimestampQueryPool.get(), q.endIndex);
}
}
void VulkanFrameBuffer::UpdateGpuStats()
{
uint64_t timestamps[MaxTimestampQueries];
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);
double timestampPeriod = device->PhysicalDevice.Properties.limits.timestampPeriod;
gpuStatOutput = "";
for (auto &q : timeElapsedQueries)
{
if (q.endIndex <= q.startIndex)
continue;
int64_t timeElapsed = 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;
}
void VulkanFrameBuffer::Draw2D()
{
::Draw2D(twod, *mRenderState);
}
VulkanCommandBuffer *VulkanFrameBuffer::GetTransferCommands()
{
if (!mTransferCommands)
{
mTransferCommands = mCommandPool->createBuffer();
mTransferCommands->SetDebugName("VulkanFrameBuffer.mTransferCommands");
mTransferCommands->begin();
}
return mTransferCommands.get();
}
VulkanCommandBuffer *VulkanFrameBuffer::GetDrawCommands()
{
if (!mDrawCommands)
{
mDrawCommands = mCommandPool->createBuffer();
mDrawCommands->SetDebugName("VulkanFrameBuffer.mDrawCommands");
mDrawCommands->begin();
}
return mDrawCommands.get();
}
unsigned int VulkanFrameBuffer::GetLightBufferBlockSize() const
{
return mLights->GetBlockSize();
}
void VulkanFrameBuffer::PrintStartupLog()
{
const auto &props = device->PhysicalDevice.Properties;
FString deviceType;
switch (props.deviceType)
{
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;
default: deviceType.Format("%d", (int)props.deviceType); break;
}
FString apiVersion, driverVersion;
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));
Printf("Vulkan device: " TEXTCOLOR_ORANGE "%s\n", props.deviceName);
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:");
for (const VkExtensionProperties &p : device->PhysicalDevice.Extensions)
{
Printf(PRINT_LOG, " %s", p.extensionName);
}
Printf(PRINT_LOG, "\n");
const auto &limits = props.limits;
Printf("Max. texture size: %d\n", limits.maxImageDimension2D);
Printf("Max. uniform buffer range: %d\n", limits.maxUniformBufferRange);
Printf("Min. uniform buffer offset alignment: %" PRIu64 "\n", limits.minUniformBufferOffsetAlignment);
}
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(), BufferUsageType::Static);
}
void VulkanFrameBuffer::UpdateShadowMap()
{
mPostprocess->UpdateShadowMap();
}
void VulkanFrameBuffer::SetSaveBuffers(bool yes)
{
if (yes) mActiveRenderBuffers = mSaveBuffers.get();
else mActiveRenderBuffers = mScreenBuffers.get();
}
void VulkanFrameBuffer::ImageTransitionScene(bool unknown)
{
mPostprocess->ImageTransitionScene(unknown);
}
FRenderState* VulkanFrameBuffer::RenderState()
{
return mRenderState.get();
}
void VulkanFrameBuffer::AmbientOccludeScene(float m5)
{
mPostprocess->AmbientOccludeScene(m5);
}
void VulkanFrameBuffer::SetSceneRenderTarget(bool useSSAO)
{
mRenderState->SetRenderTarget(&GetBuffers()->SceneColor, GetBuffers()->SceneDepthStencil.View.get(), GetBuffers()->GetWidth(), GetBuffers()->GetHeight(), VK_FORMAT_R16G16B16A16_SFLOAT, GetBuffers()->GetSceneSamples());
}