Add a level mesh viewer (--viewer cmdline arg) for better debugging

This commit is contained in:
Magnus Norddahl 2024-03-11 04:25:30 +01:00
parent 6f779c0351
commit e28879187c
12 changed files with 973 additions and 16 deletions

View file

@ -81,13 +81,17 @@ set(ZDRAY_SOURCES
src/lightmapper/gpuraytracer.h
src/lightmapper/stacktrace.cpp
src/lightmapper/stacktrace.h
src/lightmapper/levelmeshviewer.cpp
src/lightmapper/levelmeshviewer.h
src/lightmapper/glsl/binding_lightmapper.glsl.h
src/lightmapper/glsl/binding_raytrace.glsl.h
src/lightmapper/glsl/binding_textures.glsl.h
src/lightmapper/glsl/binding_viewer.glsl.h
src/lightmapper/glsl/frag_blur.glsl.h
src/lightmapper/glsl/frag_copy.glsl.h
src/lightmapper/glsl/frag_raytrace.glsl.h
src/lightmapper/glsl/frag_resolve.glsl.h
src/lightmapper/glsl/frag_viewer.glsl.h
src/lightmapper/glsl/montecarlo.glsl.h
src/lightmapper/glsl/polyfill_rayquery.glsl.h
src/lightmapper/glsl/trace_ambient_occlusion.glsl.h
@ -98,6 +102,7 @@ set(ZDRAY_SOURCES
src/lightmapper/glsl/vert_copy.glsl.h
src/lightmapper/glsl/vert_raytrace.glsl.h
src/lightmapper/glsl/vert_screenquad.glsl.h
src/lightmapper/glsl/vert_viewer.glsl.h
src/models/model.cpp
src/models/model.h
src/models/model_md2.h

View file

@ -0,0 +1,97 @@
static const char* binding_viewer_glsl = R"glsl(
struct SurfaceInfo
{
vec3 Normal;
float Sky;
uint PortalIndex;
int TextureIndex;
float Alpha;
float Padding0;
uint LightStart;
uint LightEnd;
uint Padding1;
uint Padding2;
};
struct PortalInfo
{
mat4 Transformation;
};
struct LightInfo
{
vec3 Origin;
float Padding0;
vec3 RelativeOrigin;
float Padding1;
float Radius;
float Intensity;
float InnerAngleCos;
float OuterAngleCos;
vec3 SpotDir;
float SourceRadius;
vec3 Color;
float Padding3;
};
layout(set = 0, binding = 0, std430) buffer SurfaceIndexBuffer { uint surfaceIndices[]; };
layout(set = 0, binding = 1, std430) buffer SurfaceBuffer { SurfaceInfo surfaces[]; };
layout(set = 0, binding = 2, std430) buffer LightBuffer { LightInfo lights[]; };
layout(set = 0, binding = 3, std430) buffer LightIndexBuffer { int lightIndexes[]; };
layout(set = 0, binding = 4, std430) buffer PortalBuffer { PortalInfo portals[]; };
#if defined(USE_RAYQUERY)
layout(set = 0, binding = 5) uniform accelerationStructureEXT acc;
#else
struct CollisionNode
{
vec3 center;
float padding1;
vec3 extents;
float padding2;
int left;
int right;
int element_index;
int padding3;
};
layout(set = 0, binding = 5, std430) buffer NodeBuffer
{
int nodesRoot;
int nodebufferPadding1;
int nodebufferPadding2;
int nodebufferPadding3;
CollisionNode nodes[];
};
#endif
struct SurfaceVertex // Note: this must always match the FFlatVertex struct
{
vec3 pos;
float lindex;
vec2 uv;
vec2 luv;
};
layout(set = 0, binding = 6, std430) buffer VertexBuffer { SurfaceVertex vertices[]; };
layout(set = 0, binding = 7, std430) buffer ElementBuffer { int elements[]; };
layout(set = 1, binding = 0) uniform sampler2D textures[];
layout(push_constant) uniform PushConstants
{
mat4 ViewToWorld;
vec3 CameraPos;
float ProjX;
vec3 SunDir;
float ProjY;
vec3 SunColor;
float SunIntensity;
};
)glsl";

View file

@ -0,0 +1,51 @@
static const char* frag_viewer_glsl = R"glsl(
#include <shaders/lightmap/binding_viewer.glsl>
#include <shaders/lightmap/polyfill_rayquery.glsl>
#include <shaders/lightmap/trace_levelmesh.glsl>
#include <shaders/lightmap/trace_sunlight.glsl>
#include <shaders/lightmap/trace_light.glsl>
#include <shaders/lightmap/trace_ambient_occlusion.glsl>
layout(location = 0) in vec3 FragRay;
layout(location = 0) out vec4 fragcolor;
void main()
{
vec3 incoming = vec3(0.1);
vec3 origin = CameraPos;
vec3 L = normalize(FragRay);
TraceResult result = TraceFirstHit(origin, 0.0, L, 10000.0);
if (result.primitiveIndex != -1)
{
SurfaceInfo surface = GetSurface(result.primitiveIndex);
vec3 surfacepos = origin + L * result.t;
if (surface.Sky == 0.0)
{
incoming += TraceSunLight(surfacepos, surface.Normal);
uint LightStart = surface.LightStart;
uint LightEnd = surface.LightEnd;
for (uint j = LightStart; j < LightEnd; j++)
{
incoming += TraceLight(surfacepos, surface.Normal, lights[lightIndexes[j]], 0.0);
}
// incoming *= TraceAmbientOcclusion(surfacepos, surface.Normal);
}
else
{
incoming = SunColor;
}
}
else
{
incoming = vec3(1.0, 0.0, 0.0);
}
fragcolor = vec4(incoming, 1.0);
}
)glsl";

View file

@ -0,0 +1,24 @@
static const char* vert_viewer_glsl = R"glsl(
#include <shaders/lightmap/binding_viewer.glsl>
layout(location = 0) out vec3 FragRay;
vec2 positions[4] = vec2[](
vec2(0.0, 0.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(1.0, 1.0)
);
void main()
{
vec4 viewpos = vec4(positions[gl_VertexIndex] * 2.0 - 1.0, 1.0, 1.0);
viewpos.x /= ProjX;
viewpos.y = -viewpos.y / ProjY;
FragRay = (ViewToWorld * viewpos).xyz - CameraPos;
gl_Position = vec4(positions[gl_VertexIndex] * 2.0 - 1.0, 1.0, 1.0);
}
)glsl";

View file

@ -5,6 +5,7 @@
#include "vk_lightmapper.h"
#include "renderdoc_app.h"
#include "doom_levelmesh.h"
#include "levelmeshviewer.h"
#ifndef _WIN32
#include <dlfcn.h>
@ -12,10 +13,14 @@
static RENDERDOC_API_1_4_2* rdoc_api;
extern bool showviewer;
GPURaytracer::GPURaytracer()
{
LoadRenderDoc();
mDevice = std::make_unique<VulkanRenderDevice>();
if (showviewer)
mViewer = std::make_unique<LevelMeshViewer>();
mDevice = std::make_unique<VulkanRenderDevice>(mViewer.get());
PrintVulkanInfo();
}
@ -77,6 +82,9 @@ void GPURaytracer::Raytrace(DoomLevelMesh* mesh)
{
mDevice->GetTextureManager()->DownloadLightmap(arrayIndex, mesh->LMTextureData.Data() + arrayIndex * mesh->LMTextureSize * mesh->LMTextureSize * 4);
}
if (mViewer)
mViewer->Exec(mDevice.get(), mesh->SunDirection, mesh->SunColor, 1.0f);
}
catch (...)
{

View file

@ -3,6 +3,7 @@
#include "vk_renderdevice.h"
class DoomLevelMesh;
class LevelMeshViewer;
class GPURaytracer
{
@ -16,5 +17,6 @@ private:
void PrintVulkanInfo();
void LoadRenderDoc();
std::unique_ptr<LevelMeshViewer> mViewer;
std::unique_ptr<VulkanRenderDevice> mDevice;
};

View file

@ -9,6 +9,15 @@ LevelMesh::LevelMesh()
AddEmptyMesh();
UpdateCollision();
Mesh.MaxVertices = std::max(Mesh.Vertices.Size() * 2, (unsigned int)10000);
Mesh.MaxIndexes = std::max(Mesh.Indexes.Size() * 2, (unsigned int)10000);
Mesh.MaxSurfaces = std::max(Mesh.SurfaceIndexes.Size() * 2, (unsigned int)10000);
Mesh.MaxUniforms = std::max(Mesh.Uniforms.Size() * 2, (unsigned int)10000);
Mesh.MaxSurfaceIndexes = std::max(Mesh.SurfaceIndexes.Size() * 2, (unsigned int)10000);
Mesh.MaxNodes = (int)std::max(Collision->get_nodes().size() * 2, (size_t)10000);
Mesh.MaxLights = 100'000;
Mesh.MaxLightIndexes = 4 * 1024 * 1024;
}
void LevelMesh::AddEmptyMesh()

View file

@ -0,0 +1,326 @@
#ifdef WIN32
#include "levelmeshviewer.h"
#include "vk_renderdevice.h"
#include "framework/matrix.h"
#include <windowsx.h>
#include <stdexcept>
#include <zvulkan/vulkanswapchain.h>
LevelMeshViewer::LevelMeshViewer()
{
CreateViewerWindow();
}
LevelMeshViewer::~LevelMeshViewer()
{
DestroyViewerWindow();
}
void LevelMeshViewer::RenderFrame()
{
RECT clientRect = {};
GetClientRect(WindowHandle, &clientRect);
int width = clientRect.right;
int height = clientRect.bottom;
if (width <= 0 || height <= 0)
{
Sleep(500);
return;
}
RenderDevice->ResizeSwapChain(width, height);
VSMatrix movematrix;
movematrix.loadIdentity();
movematrix.rotate((float)Yaw, 0.0f, 1.0f, 0.0);
movematrix.rotate((float)Pitch, 1.0f, 0.0f, 0.0);
if (MoveLeft)
{
CameraPos += (movematrix * FVector4(-10.0f, 0.0f, 0.0f, 1.0f)).XYZ();
}
if (MoveRight)
{
CameraPos += (movematrix * FVector4(10.0f, 0.0f, 0.0f, 1.0f)).XYZ();
}
if (MoveForward)
{
CameraPos += (movematrix * FVector4(0.0f, 0.0f, 10.0f, 1.0f)).XYZ();
}
if (MoveBackward)
{
CameraPos += (movematrix * FVector4(0.0f, 0.0f, -10.0f, 1.0f)).XYZ();
}
VSMatrix worldToView;
worldToView.loadIdentity();
worldToView.rotate((float)-Pitch, 1.0f, 0.0f, 0.0);
worldToView.rotate((float)-Yaw, 0.0f, 1.0f, 0.0);
worldToView.translate(-CameraPos.X, -CameraPos.Y, -CameraPos.Z);
VSMatrix viewToWorld;
worldToView.inverseMatrix(viewToWorld);
RenderDevice->DrawViewer(CameraPos, viewToWorld, 60.0f, width / (float)height, SunDir, SunColor, SunIntensity);
}
void LevelMeshViewer::OnMouseMove(double dx, double dy)
{
Yaw += dx * 0.1;
while (Yaw < 0.0) Yaw += 360.0;
while (Yaw >= 360.0) Yaw -= 360.0;
Pitch += dy * 0.1;
if (Pitch < -90.0) Pitch = -90.0;
if (Pitch > 90.0) Pitch = 90.0;
}
void LevelMeshViewer::OnKeyDown(WPARAM key)
{
if (key == 'A')
{
MoveLeft = true;
}
else if (key == 'D')
{
MoveRight = true;
}
else if (key == 'W')
{
MoveForward = true;
}
else if (key == 'S')
{
MoveBackward = true;
}
}
void LevelMeshViewer::OnKeyUp(WPARAM key)
{
if (key == 'A')
{
MoveLeft = false;
}
else if (key == 'D')
{
MoveRight = false;
}
else if (key == 'W')
{
MoveForward = false;
}
else if (key == 'S')
{
MoveBackward = false;
}
}
void LevelMeshViewer::Exec(VulkanRenderDevice* renderdevice, const FVector3& sundir, const FVector3& suncolor, float sunintensity)
{
RenderDevice = renderdevice;
SunDir = sundir;
SunColor = suncolor;
SunIntensity = sunintensity;
bool exitFlag = false;
while (!exitFlag)
{
MSG msg = {};
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
exitFlag = true;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
RenderFrame();
}
ShowWindow(WindowHandle, SW_HIDE);
}
void LevelMeshViewer::CreateViewerWindow()
{
WNDCLASSEX classdesc = {};
classdesc.cbSize = sizeof(WNDCLASSEX);
classdesc.hInstance = GetModuleHandle(nullptr);
classdesc.lpszClassName = L"LevelMeshViewerWindow";
classdesc.lpfnWndProc = &LevelMeshViewer::WindowProc;
classdesc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS;
classdesc.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
BOOL result = RegisterClassEx(&classdesc);
if (!result)
throw std::runtime_error("RegisterClassEx failed");
HDC screenDC = GetDC(0);
int dpi = GetDeviceCaps(screenDC, LOGPIXELSX);
ReleaseDC(0, screenDC);
auto applyDpi = [=](int i) { return (i * dpi + (96 / 2)) / 96; };
// Create and show the window
WindowHandle = CreateWindowEx(WS_EX_APPWINDOW, L"LevelMeshViewerWindow", L"ZDRay Viewer", WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE, applyDpi(100), applyDpi(50), applyDpi(1400), applyDpi(800), 0, 0, GetModuleHandle(nullptr), this);
if (!WindowHandle)
throw std::runtime_error("CreateWindowEx failed");
}
void LevelMeshViewer::DestroyViewerWindow()
{
if (WindowHandle)
DestroyWindow(WindowHandle);
WindowHandle = 0;
}
void LevelMeshViewer::OnClose()
{
PostQuitMessage(0);
}
LRESULT LevelMeshViewer::OnWindowMessage(UINT msg, WPARAM wparam, LPARAM lparam)
{
/*if (msg == WM_RBUTTONDOWN)
{
if (!MouseLocked)
{
GetCursorPos(&LockPosition);
::ShowCursor(FALSE);
RECT box = {};
GetClientRect(WindowHandle, &box);
POINT center = {};
center.x = box.right / 2;
center.y = box.bottom / 2;
ClientToScreen(WindowHandle, &center);
SetCursorPos(center.x, center.y);
MouseLocked = true;
}
}
else if (msg == WM_RBUTTONUP)
{
if (MouseLocked)
{
MouseLocked = false;
SetCursorPos(LockPosition.x, LockPosition.y);
::ShowCursor(TRUE);
}
}
else*/ if (msg == WM_MOUSEMOVE)
{
if (MouseLocked && GetFocus() != 0)
{
RECT box = {};
GetClientRect(WindowHandle, &box);
POINT center = {};
center.x = box.right / 2;
center.y = box.bottom / 2;
int mousex = GET_X_LPARAM(lparam) - center.x;
int mousey = GET_Y_LPARAM(lparam) - center.y;
ClientToScreen(WindowHandle, &center);
SetCursorPos(center.x, center.y);
double dpiscale = GetDpiScale();
OnMouseMove(mousex / dpiscale, mousey / dpiscale);
}
else
{
SetCursor((HCURSOR)LoadImage(0, IDC_ARROW, IMAGE_CURSOR, LR_DEFAULTSIZE, LR_DEFAULTSIZE, LR_SHARED));
}
}
else if (msg == WM_SETFOCUS)
{
if (MouseLocked)
{
GetCursorPos(&LockPosition);
::ShowCursor(FALSE);
RECT box = {};
GetClientRect(WindowHandle, &box);
POINT center = {};
center.x = box.right / 2;
center.y = box.bottom / 2;
ClientToScreen(WindowHandle, &center);
SetCursorPos(center.x, center.y);
}
}
else if (msg == WM_KILLFOCUS)
{
if (MouseLocked)
{
SetCursorPos(LockPosition.x, LockPosition.y);
::ShowCursor(TRUE);
}
}
else if (msg == WM_CLOSE)
{
OnClose();
return 0;
}
else if (msg == WM_KEYDOWN)
{
OnKeyDown(wparam);
}
else if (msg == WM_KEYUP)
{
OnKeyUp(wparam);
}
return DefWindowProc(WindowHandle, msg, wparam, lparam);
}
double LevelMeshViewer::GetDpiScale() const
{
return GetDpiForWindow(WindowHandle) / 96.0;
}
int LevelMeshViewer::GetPixelWidth() const
{
RECT box = {};
GetClientRect(WindowHandle, &box);
return box.right;
}
int LevelMeshViewer::GetPixelHeight() const
{
RECT box = {};
GetClientRect(WindowHandle, &box);
return box.bottom;
}
LRESULT LevelMeshViewer::WindowProc(HWND windowhandle, UINT msg, WPARAM wparam, LPARAM lparam)
{
if (msg == WM_CREATE)
{
CREATESTRUCT* createstruct = (CREATESTRUCT*)lparam;
LevelMeshViewer* viewport = (LevelMeshViewer*)createstruct->lpCreateParams;
viewport->WindowHandle = windowhandle;
SetWindowLongPtr(windowhandle, GWLP_USERDATA, (LONG_PTR)viewport);
return viewport->OnWindowMessage(msg, wparam, lparam);
}
else
{
LevelMeshViewer* viewport = (LevelMeshViewer*)GetWindowLongPtr(windowhandle, GWLP_USERDATA);
if (viewport)
{
LRESULT result = viewport->OnWindowMessage(msg, wparam, lparam);
if (msg == WM_DESTROY)
{
SetWindowLongPtr(windowhandle, GWLP_USERDATA, 0);
viewport->WindowHandle = 0;
}
return result;
}
else
{
return DefWindowProc(windowhandle, msg, wparam, lparam);
}
}
}
#endif

View file

@ -0,0 +1,67 @@
#pragma once
#include "framework/vectors.h"
class VulkanRenderDevice;
#ifdef WIN32
#define WIN32_MEAN_AND_LEAN
#define NOMINMAX
#include <Windows.h>
class LevelMeshViewer
{
public:
LevelMeshViewer();
~LevelMeshViewer();
void Exec(VulkanRenderDevice* renderdevice, const FVector3& sundir, const FVector3& suncolor, float sunintensity);
HWND GetWindowHandle() { return WindowHandle; }
private:
void RenderFrame();
void OnMouseMove(double dx, double dy);
void OnKeyDown(WPARAM key);
void OnKeyUp(WPARAM key);
void OnClose();
void CreateViewerWindow();
void DestroyViewerWindow();
double GetDpiScale() const;
int GetPixelWidth() const;
int GetPixelHeight() const;
LRESULT OnWindowMessage(UINT msg, WPARAM wparam, LPARAM lparam);
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
VulkanRenderDevice* RenderDevice = nullptr;
HWND WindowHandle = 0;
bool MouseLocked = true;
POINT LockPosition = {};
double Yaw = 0.0;
double Pitch = 0.0;
bool MoveLeft = false;
bool MoveRight = false;
bool MoveForward = false;
bool MoveBackward = false;
FVector3 CameraPos = FVector3(0.0f, 70.0f, -128.0f);
FVector3 SunDir = FVector3(0.0f, 0.0f, 0.0f);
FVector3 SunColor = FVector3(0.0f, 0.0f, 0.0f);
float SunIntensity = 0.0f;
};
#else
class LevelMeshViewer
{
public:
LevelMeshViewer() { }
void Exec(VulkanRenderDevice* renderdevice) { }
};
#endif

View file

@ -3,8 +3,21 @@
#include "vk_levelmesh.h"
#include "vk_lightmapper.h"
#include "stacktrace.h"
#include "levelmeshviewer.h"
#include "framework/matrix.h"
#include "glsl/vert_viewer.glsl.h"
#include "glsl/frag_viewer.glsl.h"
#include "glsl/binding_viewer.glsl.h"
#include "glsl/polyfill_rayquery.glsl.h"
#include "glsl/montecarlo.glsl.h"
#include "glsl/trace_ambient_occlusion.glsl.h"
#include "glsl/trace_bounce.glsl.h"
#include "glsl/trace_levelmesh.glsl.h"
#include "glsl/trace_light.glsl.h"
#include "glsl/trace_sunlight.glsl.h"
#include <zvulkan/vulkanbuilders.h>
#include <zvulkan/vulkancompatibledevice.h>
#include <zvulkan/vulkanswapchain.h>
#include <stdexcept>
extern bool VKDebug;
@ -21,7 +34,33 @@ void VulkanPrintLog(const char* typestr, const std::string& msg)
printf(" %s\n", CaptureStackTraceText(2).c_str());
}
VulkanRenderDevice::VulkanRenderDevice()
VulkanRenderDevice::VulkanRenderDevice(LevelMeshViewer* viewer)
{
if (viewer)
{
#ifdef WIN32
auto instance = VulkanInstanceBuilder()
.RequireSurfaceExtensions()
.DebugLayer(VKDebug)
.Create();
auto surface = VulkanSurfaceBuilder()
.Win32Window(viewer->GetWindowHandle())
.Create(instance);
device = VulkanDeviceBuilder()
.Surface(surface)
.OptionalRayQuery()
.RequireExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME)
.Create(instance);
swapchain = VulkanSwapChainBuilder()
.Create(device.get());
#else
VulkanError("No viewer supported on this platform");
#endif
}
else
{
auto instance = VulkanInstanceBuilder()
.DebugLayer(VKDebug)
@ -31,6 +70,7 @@ VulkanRenderDevice::VulkanRenderDevice()
.OptionalRayQuery()
.RequireExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME)
.Create(instance);
}
useRayQuery = !NoRtx && device->PhysicalDevice.Features.RayQuery.rayQuery;
@ -44,6 +84,8 @@ VulkanRenderDevice::VulkanRenderDevice()
descriptors->AddBindlessTextureIndex(GetTextureManager()->GetNullTextureView(), GetSamplerManager()->Get());
descriptors->AddBindlessTextureIndex(GetTextureManager()->GetNullTextureView(), GetSamplerManager()->Get());
descriptors->UpdateBindlessDescriptorSet();
CreateViewerObjects();
}
VulkanRenderDevice::~VulkanRenderDevice()
@ -51,6 +93,238 @@ VulkanRenderDevice::~VulkanRenderDevice()
vkDeviceWaitIdle(device->device);
}
void VulkanRenderDevice::DrawViewer(const FVector3& cameraPos, const VSMatrix& viewToWorld, float fovy, float aspect, const FVector3& sundir, const FVector3& suncolor, float sunintensity)
{
int imageIndex = GetCommands()->AcquireImage();
if (imageIndex < 0)
{
Sleep(500);
return;
}
WriteDescriptors write;
write.AddBuffer(Viewer.DescriptorSet.get(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetSurfaceIndexBuffer());
write.AddBuffer(Viewer.DescriptorSet.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetSurfaceBuffer());
write.AddBuffer(Viewer.DescriptorSet.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetLightBuffer());
write.AddBuffer(Viewer.DescriptorSet.get(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetLightIndexBuffer());
write.AddBuffer(Viewer.DescriptorSet.get(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetPortalBuffer());
if (useRayQuery)
{
write.AddAccelerationStructure(Viewer.DescriptorSet.get(), 5, GetLevelMesh()->GetAccelStruct());
}
else
{
write.AddBuffer(Viewer.DescriptorSet.get(), 5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetNodeBuffer());
}
write.AddBuffer(Viewer.DescriptorSet.get(), 6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetVertexBuffer());
write.AddBuffer(Viewer.DescriptorSet.get(), 7, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, GetLevelMesh()->GetIndexBuffer());
write.Execute(device.get());
auto commands = GetCommands()->GetDrawCommands();
RenderPassBegin()
.RenderPass(Viewer.RenderPass.get())
.Framebuffer(Framebuffers[imageIndex].get())
.RenderArea(0, 0, CurrentWidth, CurrentHeight)
.AddClearColor(0.0f, 0.0f, 0.0f, 1.0f)
.AddClearDepth(1.0f)
.Execute(commands);
VkViewport viewport = {};
viewport.width = (float)CurrentWidth;
viewport.height = (float)CurrentHeight;
viewport.maxDepth = 1.0f;
commands->setViewport(0, 1, &viewport);
VkRect2D scissor = {};
scissor.extent.width = CurrentWidth;
scissor.extent.height = CurrentHeight;
commands->setScissor(0, 1, &scissor);
float f = 1.0f / std::tan(fovy * (pi::pif() / 360.0f));
ViewerPushConstants pushconstants;
pushconstants.ViewToWorld = viewToWorld;
pushconstants.CameraPos = cameraPos;
pushconstants.ProjX = f / aspect;
pushconstants.ProjY = f;
pushconstants.SunDir = sundir;
pushconstants.SunColor = suncolor;
pushconstants.SunIntensity = sunintensity;
commands->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, Viewer.PipelineLayout.get(), 0, Viewer.DescriptorSet.get());
commands->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, Viewer.PipelineLayout.get(), 1, descriptors->GetBindlessSet());
commands->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, Viewer.Pipeline.get());
commands->pushConstants(Viewer.PipelineLayout.get(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(ViewerPushConstants), &pushconstants);
commands->draw(4, 1, 0, 0);
commands->endRenderPass();
GetCommands()->SubmitAndWait(imageIndex);
}
static FString LoadPrivateShaderLump(const char* lumpname)
{
static std::map<FString, FString> sources =
{
{ "shaders/lightmap/binding_viewer.glsl", binding_viewer_glsl },
{ "shaders/lightmap/montecarlo.glsl", montecarlo_glsl },
{ "shaders/lightmap/polyfill_rayquery.glsl", polyfill_rayquery_glsl },
{ "shaders/lightmap/trace_ambient_occlusion.glsl", trace_ambient_occlusion_glsl },
{ "shaders/lightmap/trace_bounce.glsl", trace_bounce_glsl },
{ "shaders/lightmap/trace_levelmesh.glsl", trace_levelmesh_glsl },
{ "shaders/lightmap/trace_light.glsl", trace_light_glsl },
{ "shaders/lightmap/trace_sunlight.glsl", trace_sunlight_glsl },
};
auto it = sources.find(lumpname);
if (it != sources.end())
return it->second;
else
return FString();
}
static FString LoadPublicShaderLump(const char* lumpname)
{
return LoadPrivateShaderLump(lumpname);
}
static ShaderIncludeResult OnInclude(FString headerName, FString includerName, size_t depth, bool system)
{
if (depth > 8)
{
return ShaderIncludeResult("Too much include recursion!");
}
FString includeguardname;
includeguardname << "_HEADERGUARD_" << headerName.GetChars();
includeguardname.ReplaceChars("/\\.", '_');
FString code;
code << "#ifndef " << includeguardname.GetChars() << "\n";
code << "#define " << includeguardname.GetChars() << "\n";
code << "#line 1\n";
if (system)
code << LoadPrivateShaderLump(headerName.GetChars()).GetChars() << "\n";
else
code << LoadPublicShaderLump(headerName.GetChars()).GetChars() << "\n";
code << "#endif\n";
return ShaderIncludeResult(headerName.GetChars(), code.GetChars());
}
void VulkanRenderDevice::CreateViewerObjects()
{
DescriptorSetLayoutBuilder builder;
builder.AddBinding(0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
if (useRayQuery)
{
builder.AddBinding(5, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
}
else
{
builder.AddBinding(5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
}
builder.AddBinding(6, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.AddBinding(7, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT);
builder.DebugName("Viewer.DescriptorSetLayout");
Viewer.DescriptorSetLayout = builder.Create(device.get());
Viewer.DescriptorPool = DescriptorPoolBuilder()
.AddPoolSize(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1)
.AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 6)
.MaxSets(1)
.DebugName("Viewer.DescriptorPool")
.Create(device.get());
Viewer.DescriptorSet = Viewer.DescriptorPool->allocate(Viewer.DescriptorSetLayout.get());
Viewer.DescriptorSet->SetDebugName("raytrace.descriptorSet1");
std::string versionBlock = R"(
#version 460
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_nonuniform_qualifier : enable
)";
if (useRayQuery)
{
versionBlock += "#extension GL_EXT_ray_query : require\r\n";
versionBlock += "#define USE_RAYQUERY\r\n";
}
auto onIncludeLocal = [](std::string headerName, std::string includerName, size_t depth) { return OnInclude(headerName.c_str(), includerName.c_str(), depth, false); };
auto onIncludeSystem = [](std::string headerName, std::string includerName, size_t depth) { return OnInclude(headerName.c_str(), includerName.c_str(), depth, true); };
Viewer.VertexShader = ShaderBuilder()
.Type(ShaderType::Vertex)
.AddSource("versionblock", versionBlock)
.AddSource("vert_viewer.glsl", vert_viewer_glsl)
.OnIncludeLocal(onIncludeLocal)
.OnIncludeSystem(onIncludeSystem)
.DebugName("Viewer.VertexShader")
.Create("vertex", device.get());
Viewer.FragmentShader = ShaderBuilder()
.Type(ShaderType::Fragment)
.AddSource("versionblock", versionBlock)
.AddSource("frag_viewer.glsl", frag_viewer_glsl)
.OnIncludeLocal(onIncludeLocal)
.OnIncludeSystem(onIncludeSystem)
.DebugName("Viewer.FragmentShader")
.Create("vertex", device.get());
Viewer.PipelineLayout = PipelineLayoutBuilder()
.AddSetLayout(Viewer.DescriptorSetLayout.get())
.AddSetLayout(descriptors->GetBindlessLayout())
.AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(ViewerPushConstants))
.DebugName("Viewer.PipelineLayout")
.Create(device.get());
Viewer.RenderPass = RenderPassBuilder()
.AddAttachment(VK_FORMAT_B8G8R8A8_UNORM, VK_SAMPLE_COUNT_1_BIT, VK_ATTACHMENT_LOAD_OP_CLEAR, VK_ATTACHMENT_STORE_OP_STORE, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
.AddSubpass()
.AddSubpassColorAttachmentRef(0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
.Create(device.get());
Viewer.Pipeline = GraphicsPipelineBuilder()
.RenderPass(Viewer.RenderPass.get())
.Layout(Viewer.PipelineLayout.get())
.Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP)
.AddVertexShader(Viewer.VertexShader.get())
.AddFragmentShader(Viewer.FragmentShader.get())
.AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT)
.AddDynamicState(VK_DYNAMIC_STATE_SCISSOR)
.DebugName("Viewer.Pipeline")
.Create(device.get());
}
void VulkanRenderDevice::ResizeSwapChain(int width, int height)
{
if (width <= 0 || height <= 0 || (width == CurrentWidth && height == CurrentHeight && swapchain->Lost()))
return;
CurrentWidth = width;
CurrentHeight = height;
Framebuffers.clear();
swapchain->Create(width, height, 1, true, false, false);
for (int imageIndex = 0; imageIndex < swapchain->ImageCount(); imageIndex++)
{
Framebuffers.push_back(FramebufferBuilder()
.RenderPass(Viewer.RenderPass.get())
.AddAttachment(swapchain->GetImageView(imageIndex))
.Size(width, height)
.DebugName("framebuffer")
.Create(GetDevice()));
}
}
/////////////////////////////////////////////////////////////////////////////
VkCommandBufferManager::VkCommandBufferManager(VulkanRenderDevice* fb) : fb(fb)
@ -59,9 +333,26 @@ VkCommandBufferManager::VkCommandBufferManager(VulkanRenderDevice* fb) : fb(fb)
.QueueFamily(fb->GetDevice()->GraphicsFamily)
.DebugName("mCommandPool")
.Create(fb->GetDevice());
mImageAvailableSemaphore = SemaphoreBuilder()
.DebugName("mImageAvailableSemaphore")
.Create(fb->GetDevice());
mRenderFinishedSemaphore = SemaphoreBuilder()
.DebugName("mRenderFinishedSemaphore")
.Create(fb->GetDevice());
mPresentFinishedFence = FenceBuilder()
.DebugName("mPresentFinishedFence")
.Create(fb->GetDevice());
}
void VkCommandBufferManager::SubmitAndWait()
int VkCommandBufferManager::AcquireImage()
{
return fb->GetSwapChain()->AcquireImage(mImageAvailableSemaphore.get());
}
void VkCommandBufferManager::SubmitAndWait(int imageIndex)
{
if (mTransferCommands)
{
@ -76,6 +367,24 @@ void VkCommandBufferManager::SubmitAndWait()
vkDeviceWaitIdle(fb->GetDevice()->device);
}
if (imageIndex >= 0 && mDrawCommands)
{
mDrawCommands->end();
QueueSubmit()
.AddCommandBuffer(mDrawCommands.get())
.AddWait(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, mImageAvailableSemaphore.get())
.AddSignal(mRenderFinishedSemaphore.get())
.Execute(fb->GetDevice(), fb->GetDevice()->GraphicsQueue, mPresentFinishedFence.get());
DrawDeleteList->Add(std::move(mDrawCommands));
fb->GetSwapChain()->QueuePresent(imageIndex, mRenderFinishedSemaphore.get());
vkWaitForFences(fb->GetDevice()->device, 1, &mPresentFinishedFence->fence, VK_TRUE, std::numeric_limits<uint64_t>::max());
vkResetFences(fb->GetDevice()->device, 1, &mPresentFinishedFence->fence);
}
TransferDeleteList = std::make_unique<DeleteList>();
DrawDeleteList = std::make_unique<DeleteList>();
}
@ -90,6 +399,16 @@ VulkanCommandBuffer* VkCommandBufferManager::GetTransferCommands()
return mTransferCommands.get();
}
VulkanCommandBuffer* VkCommandBufferManager::GetDrawCommands()
{
if (!mDrawCommands)
{
mDrawCommands = mCommandPool->createBuffer();
mDrawCommands->begin();
}
return mDrawCommands.get();
}
/////////////////////////////////////////////////////////////////////////////
VkTextureManager::VkTextureManager(VulkanRenderDevice* fb) : fb(fb)
@ -102,7 +421,7 @@ void VkTextureManager::CreateNullTexture()
NullTexture = ImageBuilder()
.Format(VK_FORMAT_R8G8B8A8_UNORM)
.Size(1, 1)
.Usage(VK_IMAGE_USAGE_SAMPLED_BIT)
.Usage(VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT)
.DebugName("VkTextureManager.NullTexture")
.Create(fb->GetDevice());
@ -124,7 +443,7 @@ void VkTextureManager::CreateNullTexture()
PipelineBarrier()
.AddImage(NullTexture.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_ASPECT_COLOR_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
VkBufferImageCopy region = {};
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
@ -138,7 +457,7 @@ void VkTextureManager::CreateNullTexture()
PipelineBarrier()
.AddImage(NullTexture.get(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, VK_IMAGE_ASPECT_COLOR_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
void VkTextureManager::CreateLightmap(int newLMTextureSize, int newLMTextureCount)

View file

@ -4,6 +4,7 @@
#include "framework/textureid.h"
#include "zvulkan/vulkanobjects.h"
#include "zvulkan/vulkanbuilders.h"
#include "framework/matrix.h"
#include <stdexcept>
class VkLevelMesh;
@ -12,14 +13,18 @@ class VkCommandBufferManager;
class VkDescriptorSetManager;
class VkTextureManager;
class VkSamplerManager;
class VulkanSwapChain;
class LevelMeshViewer;
class VSMatrix;
class VulkanRenderDevice
{
public:
VulkanRenderDevice();
VulkanRenderDevice(LevelMeshViewer* viewer);
~VulkanRenderDevice();
VulkanDevice* GetDevice() { return device.get(); }
VulkanSwapChain* GetSwapChain() { return swapchain.get(); }
VkCommandBufferManager* GetCommands() { return commands.get(); }
VkDescriptorSetManager* GetDescriptorSetManager() { return descriptors.get(); }
VkTextureManager* GetTextureManager() { return textures.get(); }
@ -31,16 +36,38 @@ public:
bool IsRayQueryEnabled() const { return useRayQuery; }
void ResizeSwapChain(int width, int height);
void DrawViewer(const FVector3& cameraPos, const VSMatrix& viewToWorld, float fovy, float aspect, const FVector3& sundir, const FVector3& suncolor, float sunintensity);
bool useRayQuery = false;
private:
void CreateViewerObjects();
std::shared_ptr<VulkanDevice> device;
std::shared_ptr<VulkanSwapChain> swapchain;
std::unique_ptr<VkCommandBufferManager> commands;
std::unique_ptr<VkDescriptorSetManager> descriptors;
std::unique_ptr<VkTextureManager> textures;
std::unique_ptr<VkSamplerManager> samplers;
std::unique_ptr<VkLevelMesh> levelmesh;
std::unique_ptr<VkLightmapper> lightmapper;
struct
{
std::unique_ptr<VulkanDescriptorSetLayout> DescriptorSetLayout;
std::unique_ptr<VulkanDescriptorPool> DescriptorPool;
std::unique_ptr<VulkanDescriptorSet> DescriptorSet;
std::unique_ptr<VulkanShader> VertexShader;
std::unique_ptr<VulkanShader> FragmentShader;
std::unique_ptr<VulkanRenderPass> RenderPass;
std::unique_ptr<VulkanPipelineLayout> PipelineLayout;
std::unique_ptr<VulkanPipeline> Pipeline;
} Viewer;
int CurrentWidth = 0;
int CurrentHeight = 0;
std::vector<std::unique_ptr<VulkanFramebuffer>> Framebuffers;
};
class VkCommandBufferManager
@ -48,13 +75,16 @@ class VkCommandBufferManager
public:
VkCommandBufferManager(VulkanRenderDevice* fb);
void SubmitAndWait();
void SubmitAndWait(int imageIndex = -1);
VulkanCommandBuffer* GetTransferCommands();
VulkanCommandBuffer* GetDrawCommands();
void PushGroup(VulkanCommandBuffer* cmdbuffer, const FString& name) { }
void PopGroup(VulkanCommandBuffer* cmdbuffer) { }
int AcquireImage();
class DeleteList
{
public:
@ -90,6 +120,20 @@ private:
std::unique_ptr<VulkanCommandPool> mCommandPool;
std::unique_ptr<VulkanCommandBuffer> mTransferCommands;
std::unique_ptr<VulkanCommandBuffer> mDrawCommands;
std::unique_ptr<VulkanSemaphore> mImageAvailableSemaphore;
std::unique_ptr<VulkanSemaphore> mRenderFinishedSemaphore;
std::unique_ptr<VulkanFence> mPresentFinishedFence;
};
struct ViewerPushConstants
{
VSMatrix ViewToWorld;
FVector3 CameraPos;
float ProjX;
FVector3 SunDir;
float ProjY;
FVector3 SunColor;
float SunIntensity;
};
class VkTextureImage

View file

@ -118,6 +118,7 @@ int LMDims = 1024;
bool VKDebug = false;
bool DumpMesh = false;
bool NoRtx = false;
bool showviewer = false;
int ambientSampleCount = 2048;
@ -160,10 +161,11 @@ static option long_opts[] =
{"dump-mesh", no_argument, 0, 1004},
{"preview", no_argument, 0, 1005},
{"no-rtx", no_argument, 0, 1006},
{"viewer", no_argument, 0, 1007},
{0,0,0,0}
};
static const char short_opts[] = "wVgGvbNrReEm:o:f:p:s:d:PqtzZXx5cj:S:D:::";
static const char short_opts[] = "wVgGvbNrReEm:o:f:p:s:d:PqtzZXx5cj:S:D::::";
// CODE --------------------------------------------------------------------
@ -480,6 +482,9 @@ static void ParseArgs(int argc, char **argv)
case 1006:
NoRtx = true;
break;
case 1007:
showviewer = true;
break;
case 1000:
ShowUsage();
exit(0);