Upgrade lightmapper to latest vkdoom code

This commit is contained in:
Magnus Norddahl 2024-01-16 12:43:37 +01:00
parent 08f280b54a
commit f689ea3640
28 changed files with 2716 additions and 2120 deletions

View file

@ -52,16 +52,25 @@ set(ZDRAY_SOURCES
src/nodebuilder/nodebuild.h
src/lightmapper/hw_levelmesh.cpp
src/lightmapper/hw_levelmesh.h
src/lightmapper/hw_levelmeshlight.h
src/lightmapper/hw_levelmeshportal.h
src/lightmapper/hw_levelmeshsurface.h
src/lightmapper/hw_lightmaptile.h
src/lightmapper/flatvertices.h
src/lightmapper/hw_materialstate.h
src/lightmapper/hw_surfaceuniforms.h
src/lightmapper/hw_collision.cpp
src/lightmapper/hw_collision.h
src/lightmapper/vk_renderdevice.cpp
src/lightmapper/vk_renderdevice.h
src/lightmapper/vk_lightmap.cpp
src/lightmapper/vk_lightmap.h
src/lightmapper/vk_raytrace.cpp
src/lightmapper/vk_raytrace.h
src/lightmapper/vk_levelmesh.cpp
src/lightmapper/vk_levelmesh.h
src/lightmapper/vk_lightmapper.cpp
src/lightmapper/vk_lightmapper.h
src/lightmapper/doom_levelmesh.cpp
src/lightmapper/doom_levelmesh.h
src/lightmapper/doom_levelsubmesh.cpp
src/lightmapper/doom_levelsubmesh.h
src/lightmapper/gpuraytracer.cpp
src/lightmapper/gpuraytracer.h
src/lightmapper/stacktrace.cpp

View file

@ -839,7 +839,10 @@ void FProcessor::BuildLightmaps()
Level.SetupLights();
LightmapMesh = std::make_unique<DoomLevelMesh>(Level, Level.DefaultSamples, LMDims);
printf(" Creating level mesh\n");
LightmapMesh = std::make_unique<DoomLevelMesh>(Level);
printf(" Surfaces: %d\n", LightmapMesh->StaticMesh->GetSurfaceCount());
printf(" Tiles: %d\n", (int)LightmapMesh->StaticMesh->LightmapTiles.Size());
std::unique_ptr<GPURaytracer> gpuraytracer = std::make_unique<GPURaytracer>();
gpuraytracer->Raytrace(LightmapMesh.get());

File diff suppressed because it is too large Load diff

View file

@ -7,6 +7,32 @@
struct FLevel;
class FWadWriter;
class DoomLevelMesh : public LevelMesh
{
public:
DoomLevelMesh(FLevel& doomMap);
int AddSurfaceLights(const LevelMeshSurface* surface, LevelMeshLight* list, int listMaxSize) override;
void BeginFrame(FLevel& doomMap);
bool TraceSky(const FVector3& start, FVector3 direction, float dist);
void DumpMesh(const FString& objFilename, const FString& mtlFilename) const;
void AddLightmapLump(FLevel& doomMap, FWadWriter& out);
void BuildSectorGroups(const FLevel& doomMap);
TArray<int> sectorGroup; // index is sector, value is sectorGroup
TArray<int> sectorPortals[2]; // index is sector+plane, value is index into the portal list
TArray<int> linePortals; // index is linedef, value is index into the portal list
private:
void CreatePortals(FLevel& doomMap);
void BuildLightLists(FLevel& doomMap);
void PropagateLight(FLevel& doomMap, ThingLight* light, int recursiveDepth = 0);
};
#if 0
class DoomLevelMesh : public LevelMesh
{
public:
@ -140,3 +166,5 @@ private:
static FVector3 ToFVector3(const DVector3& v) { return FVector3((float)v.X, (float)v.Y, (float)v.Z); }
static FVector4 ToFVector4(const DVector4& v) { return FVector4((float)v.X, (float)v.Y, (float)v.Z, (float)v.W); }
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,136 @@
#pragma once
#include "hw_levelmesh.h"
#include "framework/tarray.h"
#include "framework/vectors.h"
#include "framework/bounds.h"
#include "level/level.h"
#include <dp_rect_pack/dp_rect_pack.h>
#include <set>
#include <map>
typedef dp::rect_pack::RectPacker<int> RectPacker;
struct FLevel;
struct FPolyObj;
struct HWWallDispatcher;
class DoomLevelMesh;
class MeshBuilder;
enum DoomLevelMeshSurfaceType
{
ST_NONE,
ST_MIDDLESIDE,
ST_UPPERSIDE,
ST_LOWERSIDE,
ST_CEILING,
ST_FLOOR
};
struct DoomLevelMeshSurface : public LevelMeshSurface
{
DoomLevelMeshSurfaceType Type = ST_NONE;
int TypeIndex = 0;
MapSubsectorEx* Subsector = nullptr;
IntSideDef* Side = nullptr;
IntSector* ControlSector = nullptr;
int PipelineID = 0;
std::vector<ThingLight*> LightList;
};
class DoomLevelSubmesh : public LevelSubmesh
{
public:
DoomLevelSubmesh(DoomLevelMesh* mesh, FLevel& doomMap, bool staticMesh);
void Update(FLevel& doomMap, int lightmapStartIndex);
LevelMeshSurface* GetSurface(int index) override { return &Surfaces[index]; }
unsigned int GetSurfaceIndex(const LevelMeshSurface* surface) const override { return (unsigned int)(ptrdiff_t)(static_cast<const DoomLevelMeshSurface*>(surface) - Surfaces.Data()); }
int GetSurfaceCount() override { return Surfaces.Size(); }
TArray<DoomLevelMeshSurface> Surfaces;
private:
void Reset();
void CreateStaticSurfaces(FLevel& doomMap);
void CreateDynamicSurfaces(FLevel& doomMap);
void CreateSideSurfaces(std::map<LightmapTileBinding, int>& bindings, FLevel& doomMap, IntSideDef* side);
void CreateLineHorizonSurface(std::map<LightmapTileBinding, int>& bindings, FLevel& doomMap, IntSideDef* side);
void CreateFrontWallSurface(std::map<LightmapTileBinding, int>& bindings, FLevel& doomMap, IntSideDef* side);
void CreateMidWallSurface(std::map<LightmapTileBinding, int>& bindings, FLevel& doomMap, IntSideDef* side);
void Create3DFloorWallSurfaces(std::map<LightmapTileBinding, int>& bindings, FLevel& doomMap, IntSideDef* side);
void CreateTopWallSurface(std::map<LightmapTileBinding, int>& bindings, FLevel& doomMap, IntSideDef* side);
void CreateBottomWallSurface(std::map<LightmapTileBinding, int>& bindings, FLevel& doomMap, IntSideDef* side);
void SetSideTextureUVs(DoomLevelMeshSurface& surface, IntSideDef* side, WallPart texpart, float v1TopZ, float v1BottomZ, float v2TopZ, float v2BottomZ);
void CreateFloorSurface(std::map<LightmapTileBinding, int>& bindings, FLevel& doomMap, MapSubsectorEx* sub, IntSector* sector, IntSector* controlSector, int typeIndex);
void CreateCeilingSurface(std::map<LightmapTileBinding, int>& bindings, FLevel& doomMap, MapSubsectorEx* sub, IntSector* sector, IntSector* controlSector, int typeIndex);
void AddWallVertices(DoomLevelMeshSurface& surf, FFlatVertex* verts);
static bool IsTopSideSky(IntSector* frontsector, IntSector* backsector, IntSideDef* side);
static bool IsTopSideVisible(IntSideDef* side);
static bool IsBottomSideVisible(IntSideDef* side);
static bool IsSkySector(IntSector* sector, SecPlaneType plane);
static bool IsDegenerate(const FVector3& v0, const FVector3& v1, const FVector3& v2);
static FVector4 ToPlane(const FFlatVertex& pt1, const FFlatVertex& pt2, const FFlatVertex& pt3)
{
return ToPlane(FVector3(pt1.x, pt1.y, pt1.z), FVector3(pt2.x, pt2.y, pt2.z), FVector3(pt3.x, pt3.y, pt3.z));
}
static FVector4 ToPlane(const FFlatVertex& pt1, const FFlatVertex& pt2, const FFlatVertex& pt3, const FFlatVertex& pt4)
{
return ToPlane(FVector3(pt1.x, pt1.y, pt1.z), FVector3(pt2.x, pt2.y, pt2.z), FVector3(pt3.x, pt3.y, pt3.z), FVector3(pt4.x, pt4.y, pt4.z));
}
static FVector4 ToPlane(const FVector3& pt1, const FVector3& pt2, const FVector3& pt3)
{
FVector3 n = ((pt2 - pt1) ^ (pt3 - pt2)).Unit();
float d = pt1 | n;
return FVector4(n.X, n.Y, n.Z, d);
}
static FVector4 ToPlane(const FVector3& pt1, const FVector3& pt2, const FVector3& pt3, const FVector3& pt4)
{
if (pt1.ApproximatelyEquals(pt3))
{
return ToPlane(pt1, pt2, pt4);
}
else if (pt1.ApproximatelyEquals(pt2) || pt2.ApproximatelyEquals(pt3))
{
return ToPlane(pt1, pt3, pt4);
}
return ToPlane(pt1, pt2, pt3);
}
void SortIndexes();
void PackLightmapAtlas(FLevel& doomMap, int lightmapStartIndex);
enum PlaneAxis
{
AXIS_YZ = 0,
AXIS_XZ,
AXIS_XY
};
static PlaneAxis BestAxis(const FVector4& p);
BBox GetBoundsFromSurface(const LevelMeshSurface& surface) const;
void SetupTileTransform(int lightMapTextureWidth, int lightMapTextureHeight, LightmapTile& tile);
void AddSurfaceToTile(DoomLevelMeshSurface& surf, std::map<LightmapTileBinding, int>& bindings, FLevel& doomMap, uint16_t sampleDimension);
int GetSampleDimension(const DoomLevelMeshSurface& surf, uint16_t sampleDimension);
DoomLevelMesh* LevelMesh = nullptr;
bool StaticMesh = true;
};
static_assert(alignof(FVector2) == alignof(float[2]) && sizeof(FVector2) == sizeof(float) * 2);

View file

@ -0,0 +1,46 @@
#pragma once
struct FFlatVertex // Note: this must always match the SurfaceVertex struct in shaders (std430 layout rules apply)
{
float x, z, y; // world position
float lindex; // lightmap texture index
float u, v; // texture coordinates
float lu, lv; // lightmap texture coordinates
void Set(float xx, float zz, float yy, float uu, float vv)
{
x = xx;
z = zz;
y = yy;
u = uu;
v = vv;
lindex = -1.0f;
}
void Set(float xx, float zz, float yy, float uu, float vv, float llu, float llv, float llindex)
{
x = xx;
z = zz;
y = yy;
lindex = llindex;
u = uu;
v = vv;
lu = llu;
lv = llv;
}
void SetVertex(float _x, float _y, float _z = 0)
{
x = _x;
z = _y;
y = _z;
}
void SetTexCoord(float _u = 0, float _v = 0)
{
u = _u;
v = _v;
}
FVector3 fPos() const { return FVector3(x, y, z); }
};

View file

@ -12,10 +12,10 @@ struct SurfaceInfo
{
vec3 Normal;
float Sky;
float SamplingDistance;
uint PortalIndex;
int TextureIndex;
float Alpha;
float Padding;
};
struct PortalInfo

View file

@ -29,11 +29,12 @@ layout(std430, set = 1, binding = 0) buffer NodeBuffer
#endif
struct SurfaceVertex
struct SurfaceVertex // Note: this must always match the FFlatVertex struct
{
vec4 pos;
vec3 pos;
float lindex;
vec2 uv;
float Padding1, Padding2;
vec2 luv;
};
layout(std430, set = 1, binding = 1) buffer VertexBuffer { SurfaceVertex vertices[]; };

View file

@ -26,7 +26,7 @@ int TraceFirstHitTriangleT(vec3 origin, float tmin, vec3 dir, float tmax, out fl
int index = primitiveID * 3;
vec2 uv = vertices[elements[index + 1]].uv * primitiveWeights.x + vertices[elements[index + 2]].uv * primitiveWeights.y + vertices[elements[index + 0]].uv * primitiveWeights.z;
if (surface.TextureIndex < 0)
if (surface.TextureIndex == 0)
{
break;
}
@ -89,7 +89,7 @@ bool TracePoint(vec3 origin, vec3 target, float tmin, vec3 dir, float tmax)
int index = primitiveID * 3;
vec2 uv = vertices[elements[index + 1]].uv * primitiveWeights.x + vertices[elements[index + 2]].uv * primitiveWeights.y + vertices[elements[index + 0]].uv * primitiveWeights.z;
if (surface.TextureIndex < 0)
if (surface.TextureIndex == 0)
{
break;
}

View file

@ -14,7 +14,7 @@ vec3 TraceSunLight(vec3 origin, vec3 normal, int surfaceIndex)
const float minDistance = 0.01;
vec3 incoming = vec3(0.0);
const float dist = 32768.0;
const float dist = 65536.0;
#if defined(USE_SOFTSHADOWS)

View file

@ -1,8 +1,8 @@
#include "gpuraytracer.h"
#include "vk_renderdevice.h"
#include "vk_raytrace.h"
#include "vk_lightmap.h"
#include "vk_levelmesh.h"
#include "vk_lightmapper.h"
#include "renderdoc_app.h"
#include "doom_levelmesh.h"
@ -34,44 +34,44 @@ void GPURaytracer::Raytrace(DoomLevelMesh* mesh)
try
{
auto raytrace = mDevice->GetRaytrace();
auto lightmap = mDevice->GetLightmap();
auto levelmesh = mDevice->GetLevelMesh();
auto lightmapper = mDevice->GetLightmapper();
auto submesh = mesh->StaticMesh.get();
printf(" Map uses %u lightmap textures\n", submesh->LMTextureCount);
mDevice->GetTextureManager()->CreateLightmap(submesh->LMTextureSize, submesh->LMTextureCount);
raytrace->SetLevelMesh(mesh);
lightmap->SetLevelMesh(mesh);
levelmesh->SetLevelMesh(mesh);
lightmapper->SetLevelMesh(mesh);
// Keep baking until all surfaces have been processed
while (true)
{
raytrace->BeginFrame();
lightmap->BeginFrame();
levelmesh->BeginFrame();
lightmapper->BeginFrame();
TArray<LevelMeshSurface*> surfaces;
for (int i = 0, count = submesh->GetSurfaceCount(); i < count; i++)
TArray<LightmapTile*> tiles;
for (unsigned int i = 0, count = submesh->LightmapTiles.Size(); i < count; i++)
{
LevelMeshSurface* surface = submesh->GetSurface(i);
if (surface->needsUpdate)
LightmapTile* tile = &submesh->LightmapTiles[i];
if (tile->NeedsUpdate)
{
surfaces.Push(surface);
tiles.Push(tile);
}
}
if (surfaces.Size() == 0)
if (tiles.Size() == 0)
break;
printf(" Ray tracing surfaces: %u / %u\r", submesh->GetSurfaceCount() - surfaces.Size(), submesh->GetSurfaceCount());
printf(" Ray tracing tiles: %u / %u\r", submesh->LightmapTiles.Size() - tiles.Size(), submesh->LightmapTiles.Size());
lightmap->Raytrace(surfaces);
lightmapper->Raytrace(tiles);
mDevice->GetCommands()->SubmitAndWait();
}
printf(" Ray tracing surfaces: %u / %u\n", submesh->GetSurfaceCount(), submesh->GetSurfaceCount());
printf(" Ray tracing tiles: %u / %u\n", submesh->LightmapTiles.Size(), submesh->LightmapTiles.Size());
submesh->LMTextureData.Resize(submesh->LMTextureSize * submesh->LMTextureSize * submesh->LMTextureCount * 4);
for (int arrayIndex = 0; arrayIndex < submesh->LMTextureCount; arrayIndex++)

View file

@ -24,12 +24,11 @@
#include <algorithm>
#include <functional>
#include <cfloat>
#include <cstdint>
#ifndef DISABLE_SSE
#ifndef NO_SSE
#include <immintrin.h>
#endif
TriangleMeshShape::TriangleMeshShape(const FVector3 *vertices, int num_vertices, const unsigned int *elements, int num_elements)
TriangleMeshShape::TriangleMeshShape(const FFlatVertex *vertices, int num_vertices, const unsigned int *elements, int num_elements)
: vertices(vertices), num_vertices(num_vertices), elements(elements), num_elements(num_elements)
{
int num_triangles = num_elements / 3;
@ -45,7 +44,7 @@ TriangleMeshShape::TriangleMeshShape(const FVector3 *vertices, int num_vertices,
triangles.push_back(i);
int element_index = i * 3;
FVector3 centroid = (vertices[elements[element_index + 0]] + vertices[elements[element_index + 1]] + vertices[elements[element_index + 2]]) * (1.0f / 3.0f);
FVector3 centroid = (vertices[elements[element_index + 0]].fPos() + vertices[elements[element_index + 1]].fPos() + vertices[elements[element_index + 2]].fPos()) * (1.0f / 3.0f);
centroids.push_back(centroid);
}
@ -281,12 +280,12 @@ float TriangleMeshShape::intersect_triangle_ray(TriangleMeshShape *shape, const
FVector3 p[3] =
{
shape->vertices[shape->elements[start_element]],
shape->vertices[shape->elements[start_element + 1]],
shape->vertices[shape->elements[start_element + 2]]
shape->vertices[shape->elements[start_element]].fPos(),
shape->vertices[shape->elements[start_element + 1]].fPos(),
shape->vertices[shape->elements[start_element + 2]].fPos()
};
// Moeller<EFBFBD>Trumbore ray-triangle intersection algorithm:
// MoellerTrumbore ray-triangle intersection algorithm:
FVector3 D = ray.end - ray.start;
@ -357,9 +356,9 @@ float TriangleMeshShape::sweep_intersect_triangle_sphere(TriangleMeshShape *shap
FVector3 p[3] =
{
shape1->vertices[shape1->elements[start_element]],
shape1->vertices[shape1->elements[start_element + 1]],
shape1->vertices[shape1->elements[start_element + 2]]
shape1->vertices[shape1->elements[start_element]].fPos(),
shape1->vertices[shape1->elements[start_element + 1]].fPos(),
shape1->vertices[shape1->elements[start_element + 2]].fPos()
};
FVector3 c = shape2->center;
@ -529,9 +528,9 @@ bool TriangleMeshShape::overlap_triangle_sphere(TriangleMeshShape *shape1, Spher
int element_index = shape1->nodes[shape1_node_index].element_index;
FVector3 P = shape2->center;
FVector3 A = shape1->vertices[shape1->elements[element_index]] - P;
FVector3 B = shape1->vertices[shape1->elements[element_index + 1]] - P;
FVector3 C = shape1->vertices[shape1->elements[element_index + 2]] - P;
FVector3 A = shape1->vertices[shape1->elements[element_index]].fPos() - P;
FVector3 B = shape1->vertices[shape1->elements[element_index + 1]].fPos() - P;
FVector3 C = shape1->vertices[shape1->elements[element_index + 2]].fPos() - P;
float r = shape2->radius;
float rr = r * r;
@ -641,14 +640,14 @@ int TriangleMeshShape::subdivide(int *triangles, int num_triangles, const FVecto
// Find bounding box and median of the triangle centroids
FVector3 median;
FVector3 min, max;
min = vertices[elements[triangles[0] * 3]];
min = vertices[elements[triangles[0] * 3]].fPos();
max = min;
for (int i = 0; i < num_triangles; i++)
{
int element_index = triangles[i] * 3;
for (int j = 0; j < 3; j++)
{
const FVector3 &vertex = vertices[elements[element_index + j]];
const FVector3 &vertex = vertices[elements[element_index + j]].fPos();
min.X = std::min(min.X, vertex.X);
min.Y = std::min(min.Y, vertex.Y);
@ -785,7 +784,7 @@ static const uint32_t clearsignbitmask[] = { 0x7fffffff, 0x7fffffff, 0x7fffffff,
IntersectionTest::OverlapResult IntersectionTest::ray_aabb(const RayBBox &ray, const CollisionBBox &aabb)
{
#ifndef DISABLE_SSE
#ifndef NO_SSE
__m128 v = _mm_loadu_ps(&ray.v.X);
__m128 w = _mm_loadu_ps(&ray.w.X);

View file

@ -23,6 +23,7 @@
#pragma once
#include "framework/vectors.h"
#include "flatvertices.h"
#include <vector>
#include <cmath>
@ -86,7 +87,7 @@ public:
class TriangleMeshShape
{
public:
TriangleMeshShape(const FVector3 *vertices, int num_vertices, const unsigned int *elements, int num_elements);
TriangleMeshShape(const FFlatVertex *vertices, int num_vertices, const unsigned int *elements, int num_elements);
int get_min_depth() const;
int get_max_depth() const;
@ -121,7 +122,7 @@ public:
int get_root() const { return root; }
private:
const FVector3 *vertices = nullptr;
const FFlatVertex* vertices = nullptr;
const int num_vertices = 0;
const unsigned int *elements = nullptr;
int num_elements = 0;

View file

@ -1,6 +1,13 @@
#include "hw_levelmesh.h"
LevelMesh::LevelMesh()
{
// Default portal
LevelMeshPortal portal;
Portals.Push(portal);
}
LevelMeshSurface* LevelMesh::Trace(const FVector3& start, FVector3 direction, float maxDist)
{
maxDist = std::max(maxDist - 10.0f, 0.0f);
@ -24,15 +31,15 @@ LevelMeshSurface* LevelMesh::Trace(const FVector3& start, FVector3 direction, fl
return nullptr;
}
hitSurface = hitmesh->GetSurface(hitmesh->MeshSurfaceIndexes[hit.triangle]);
auto portal = hitSurface->portalIndex;
hitSurface = hitmesh->GetSurface(hitmesh->Mesh.SurfaceIndexes[hit.triangle]);
int portal = hitSurface->PortalIndex;
if (!portal)
{
break;
}
auto& transformation = hitmesh->Portals[portal];
auto& transformation = Portals[portal];
auto travelDist = hit.fraction * maxDist + 2.0f;
if (travelDist >= maxDist)
@ -48,11 +55,11 @@ LevelMeshSurface* LevelMesh::Trace(const FVector3& start, FVector3 direction, fl
return hitSurface; // I hit something
}
LevelMeshSurfaceStats LevelMesh::GatherSurfacePixelStats()
LevelMeshTileStats LevelMesh::GatherTilePixelStats()
{
LevelMeshSurfaceStats stats;
StaticMesh->GatherSurfacePixelStats(stats);
DynamicMesh->GatherSurfacePixelStats(stats);
LevelMeshTileStats stats;
StaticMesh->GatherTilePixelStats(stats);
DynamicMesh->GatherTilePixelStats(stats);
return stats;
}
@ -60,131 +67,123 @@ LevelMeshSurfaceStats LevelMesh::GatherSurfacePixelStats()
LevelSubmesh::LevelSubmesh()
{
// Default portal
LevelMeshPortal portal;
Portals.Push(portal);
// Default empty mesh (we can't make it completely empty since vulkan doesn't like that)
float minval = -100001.0f;
float maxval = -100000.0f;
MeshVertices.Push({ minval, minval, minval });
MeshVertices.Push({ maxval, minval, minval });
MeshVertices.Push({ maxval, maxval, minval });
MeshVertices.Push({ minval, minval, minval });
MeshVertices.Push({ minval, maxval, minval });
MeshVertices.Push({ maxval, maxval, minval });
MeshVertices.Push({ minval, minval, maxval });
MeshVertices.Push({ maxval, minval, maxval });
MeshVertices.Push({ maxval, maxval, maxval });
MeshVertices.Push({ minval, minval, maxval });
MeshVertices.Push({ minval, maxval, maxval });
MeshVertices.Push({ maxval, maxval, maxval });
MeshVertexUVs.Resize(MeshVertices.Size());
Mesh.Vertices.Push({ minval, minval, minval });
Mesh.Vertices.Push({ maxval, minval, minval });
Mesh.Vertices.Push({ maxval, maxval, minval });
Mesh.Vertices.Push({ minval, minval, minval });
Mesh.Vertices.Push({ minval, maxval, minval });
Mesh.Vertices.Push({ maxval, maxval, minval });
Mesh.Vertices.Push({ minval, minval, maxval });
Mesh.Vertices.Push({ maxval, minval, maxval });
Mesh.Vertices.Push({ maxval, maxval, maxval });
Mesh.Vertices.Push({ minval, minval, maxval });
Mesh.Vertices.Push({ minval, maxval, maxval });
Mesh.Vertices.Push({ maxval, maxval, maxval });
for (int i = 0; i < 3 * 4; i++)
MeshElements.Push(i);
Mesh.Indexes.Push(i);
UpdateCollision();
}
void LevelSubmesh::UpdateCollision()
{
Collision = std::make_unique<TriangleMeshShape>(MeshVertices.Data(), MeshVertices.Size(), MeshElements.Data(), MeshElements.Size());
Collision = std::make_unique<TriangleMeshShape>(Mesh.Vertices.Data(), Mesh.Vertices.Size(), Mesh.Indexes.Data(), Mesh.Indexes.Size());
}
void LevelSubmesh::GatherSurfacePixelStats(LevelMeshSurfaceStats& stats)
void LevelSubmesh::GatherTilePixelStats(LevelMeshTileStats& stats)
{
int count = GetSurfaceCount();
for (int i = 0; i < count; ++i)
for (const LightmapTile& tile : LightmapTiles)
{
const auto* surface = GetSurface(i);
auto area = surface->Area();
auto area = tile.AtlasLocation.Area();
stats.pixels.total += area;
if (surface->needsUpdate)
if (tile.NeedsUpdate)
{
stats.surfaces.dirty++;
stats.tiles.dirty++;
stats.pixels.dirty += area;
}
if (surface->bSky)
{
stats.surfaces.sky++;
stats.pixels.sky += area;
}
}
stats.surfaces.total += count;
stats.tiles.total += LightmapTiles.Size();
}
struct LevelMeshPlaneGroup
{
FVector4 plane = FVector4(0, 0, 1, 0);
int sectorGroup = 0;
std::vector<LevelMeshSurface*> surfaces;
};
void LevelSubmesh::BuildTileSurfaceLists()
{
// Smoothing group surface is to be rendered with
TArray<LevelMeshSmoothingGroup> SmoothingGroups;
TArray<int> SmoothingGroupIndexes(GetSurfaceCount());
// Plane group surface is to be rendered with
TArray<LevelMeshPlaneGroup> PlaneGroups;
TArray<int> PlaneGroupIndexes(GetSurfaceCount());
for (int i = 0, count = GetSurfaceCount(); i < count; i++)
{
auto surface = GetSurface(i);
// Is this surface in the same plane as an existing smoothing group?
int smoothingGroupIndex = -1;
// Is this surface in the same plane as an existing plane group?
int planeGroupIndex = -1;
for (size_t j = 0; j < SmoothingGroups.Size(); j++)
for (size_t j = 0; j < PlaneGroups.Size(); j++)
{
if (surface->sectorGroup == SmoothingGroups[j].sectorGroup)
if (surface->SectorGroup == PlaneGroups[j].sectorGroup)
{
float direction = SmoothingGroups[j].plane.XYZ() | surface->plane.XYZ();
float direction = PlaneGroups[j].plane.XYZ() | surface->Plane.XYZ();
if (direction >= 0.9999f && direction <= 1.001f)
{
auto point = (surface->plane.XYZ() * surface->plane.W);
auto planeDistance = (SmoothingGroups[j].plane.XYZ() | point) - SmoothingGroups[j].plane.W;
auto point = (surface->Plane.XYZ() * surface->Plane.W);
auto planeDistance = (PlaneGroups[j].plane.XYZ() | point) - PlaneGroups[j].plane.W;
float dist = std::abs(planeDistance);
if (dist <= 0.01f)
{
smoothingGroupIndex = (int)j;
planeGroupIndex = (int)j;
break;
}
}
}
}
// Surface is in a new plane. Create a smoothing group for it
if (smoothingGroupIndex == -1)
// Surface is in a new plane. Create a plane group for it
if (planeGroupIndex == -1)
{
smoothingGroupIndex = SmoothingGroups.Size();
planeGroupIndex = PlaneGroups.Size();
LevelMeshSmoothingGroup group;
group.plane = surface->plane;
group.sectorGroup = surface->sectorGroup;
SmoothingGroups.Push(group);
LevelMeshPlaneGroup group;
group.plane = surface->Plane;
group.sectorGroup = surface->SectorGroup;
PlaneGroups.Push(group);
}
SmoothingGroups[smoothingGroupIndex].surfaces.push_back(surface);
SmoothingGroupIndexes.Push(smoothingGroupIndex);
PlaneGroups[planeGroupIndex].surfaces.push_back(surface);
PlaneGroupIndexes.Push(planeGroupIndex);
}
for (auto& tile : LightmapTiles)
tile.Surfaces.Clear();
for (int i = 0, count = GetSurfaceCount(); i < count; i++)
{
auto targetSurface = GetSurface(i);
targetSurface->tileSurfaces.Clear();
for (LevelMeshSurface* surface : SmoothingGroups[SmoothingGroupIndexes[i]].surfaces)
LevelMeshSurface* targetSurface = GetSurface(i);
if (targetSurface->LightmapTileIndex < 0)
continue;
LightmapTile* tile = &LightmapTiles[targetSurface->LightmapTileIndex];
for (LevelMeshSurface* surface : PlaneGroups[PlaneGroupIndexes[i]].surfaces)
{
FVector2 minUV = ToUV(surface->bounds.min, targetSurface);
FVector2 maxUV = ToUV(surface->bounds.max, targetSurface);
FVector2 minUV = tile->ToUV(surface->Bounds.min);
FVector2 maxUV = tile->ToUV(surface->Bounds.max);
if (surface != targetSurface && (maxUV.X < 0.0f || maxUV.Y < 0.0f || minUV.X > 1.0f || minUV.Y > 1.0f))
continue; // Bounding box not visible
targetSurface->tileSurfaces.Push(surface);
tile->Surfaces.Push(surface);
}
}
}
FVector2 LevelSubmesh::ToUV(const FVector3& vert, const LevelMeshSurface* targetSurface)
{
FVector3 localPos = vert - targetSurface->translateWorldToLocal;
float u = (1.0f + (localPos | targetSurface->projLocalToU)) / (targetSurface->AtlasTile.Width + 2);
float v = (1.0f + (localPos | targetSurface->projLocalToV)) / (targetSurface->AtlasTile.Height + 2);
return FVector2(u, v);
}

View file

@ -3,214 +3,73 @@
#include "framework/tarray.h"
#include "framework/vectors.h"
#include "framework/matrix.h"
#include "framework/bounds.h"
#include "framework/textureid.h"
#include "hw_collision.h"
#include "flatvertices.h"
#include "hw_levelmeshlight.h"
#include "hw_levelmeshportal.h"
#include "hw_lightmaptile.h"
#include "hw_levelmeshsurface.h"
#include "hw_materialstate.h"
#include "hw_surfaceuniforms.h"
#include <memory>
#include <cstring>
#include <dp_rect_pack/dp_rect_pack.h>
struct LevelMeshTileStats;
typedef dp::rect_pack::RectPacker<int> RectPacker;
class LevelSubmesh;
class LevelMeshLight
struct LevelSubmeshDrawRange
{
public:
FVector3 Origin;
FVector3 RelativeOrigin;
float Radius;
float Intensity;
float InnerAngleCos;
float OuterAngleCos;
FVector3 SpotDir;
FVector3 Color;
int SectorGroup;
};
struct LevelMeshSurface
{
LevelSubmesh* Submesh = nullptr;
int numVerts = 0;
unsigned int startVertIndex = 0;
unsigned int startUvIndex = 0;
unsigned int startElementIndex = 0;
unsigned int numElements = 0;
FVector4 plane = FVector4(0.0f, 0.0f, 1.0f, 0.0f);
bool bSky = false;
// Surface location in lightmap texture
struct
{
int X = 0;
int Y = 0;
int Width = 0;
int Height = 0;
int ArrayIndex = 0;
} AtlasTile;
// True if the surface needs to be rendered into the lightmap texture before it can be used
bool needsUpdate = true;
FTextureID texture = FNullTextureID();
float alpha = 1.0;
int portalIndex = 0;
int sectorGroup = 0;
BBox bounds;
uint16_t sampleDimension = 0;
// Calculate world coordinates to UV coordinates
FVector3 translateWorldToLocal = { 0.f, 0.f, 0.f };
FVector3 projLocalToU = { 0.f, 0.f, 0.f };
FVector3 projLocalToV = { 0.f, 0.f, 0.f };
// Surfaces that are visible within the lightmap tile
TArray<LevelMeshSurface*> tileSurfaces;
uint32_t Area() const { return AtlasTile.Width * AtlasTile.Height; }
// Light list location in the lightmapper GPU buffers
struct
{
int Pos = -1;
int Count = 0;
int ResetCounter = -1;
} LightList;
};
inline float IsInFrontOfPlane(const FVector4& plane, const FVector3& point)
{
return (plane.X * point.X + plane.Y * point.Y + plane.Z * point.Z) >= plane.W;
}
struct LevelMeshSmoothingGroup
{
FVector4 plane = FVector4(0, 0, 1, 0);
int sectorGroup = 0;
std::vector<LevelMeshSurface*> surfaces;
};
struct LevelMeshPortal
{
LevelMeshPortal() { transformation.loadIdentity(); }
VSMatrix transformation;
int sourceSectorGroup = 0;
int targetSectorGroup = 0;
inline FVector3 TransformPosition(const FVector3& pos) const
{
auto v = transformation * FVector4(pos, 1.0);
return FVector3(v.X, v.Y, v.Z);
}
inline FVector3 TransformRotation(const FVector3& dir) const
{
auto v = transformation * FVector4(dir, 0.0);
return FVector3(v.X, v.Y, v.Z);
}
// Checks only transformation
inline bool IsInverseTransformationPortal(const LevelMeshPortal& portal) const
{
auto diff = portal.TransformPosition(TransformPosition(FVector3(0, 0, 0)));
return abs(diff.X) < 0.001 && abs(diff.Y) < 0.001 && abs(diff.Z) < 0.001;
}
// Checks only transformation
inline bool IsEqualTransformationPortal(const LevelMeshPortal& portal) const
{
auto diff = portal.TransformPosition(FVector3(0, 0, 0)) - TransformPosition(FVector3(0, 0, 0));
return (abs(diff.X) < 0.001 && abs(diff.Y) < 0.001 && abs(diff.Z) < 0.001);
}
// Checks transformation, source and destiantion sector groups
inline bool IsEqualPortal(const LevelMeshPortal& portal) const
{
return sourceSectorGroup == portal.sourceSectorGroup && targetSectorGroup == portal.targetSectorGroup && IsEqualTransformationPortal(portal);
}
// Checks transformation, source and destiantion sector groups
inline bool IsInversePortal(const LevelMeshPortal& portal) const
{
return sourceSectorGroup == portal.targetSectorGroup && targetSectorGroup == portal.sourceSectorGroup && IsInverseTransformationPortal(portal);
}
};
// for use with std::set to recursively go through portals and skip returning portals
struct RecursivePortalComparator
{
bool operator()(const LevelMeshPortal& a, const LevelMeshPortal& b) const
{
return !a.IsInversePortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(VSMatrix)) < 0;
}
};
// for use with std::map to reject portals which have the same effect for light rays
struct IdenticalPortalComparator
{
bool operator()(const LevelMeshPortal& a, const LevelMeshPortal& b) const
{
return !a.IsEqualPortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(VSMatrix)) < 0;
}
};
struct LevelMeshSurfaceStats
{
struct Stats
{
uint32_t total = 0, dirty = 0, sky = 0;
};
Stats surfaces, pixels;
int PipelineID;
int Start;
int Count;
};
class LevelSubmesh
{
public:
LevelSubmesh();
virtual ~LevelSubmesh() = default;
virtual LevelMeshSurface* GetSurface(int index) { return nullptr; }
virtual unsigned int GetSurfaceIndex(const LevelMeshSurface* surface) const { return 0xffffffff; }
virtual int GetSurfaceCount() { return 0; }
TArray<FVector3> MeshVertices;
TArray<FVector2> MeshVertexUVs;
TArray<uint32_t> MeshElements;
TArray<int> MeshSurfaceIndexes;
TArray<LevelMeshPortal> Portals;
struct
{
TArray<FFlatVertex> Vertices;
TArray<uint32_t> Indexes;
TArray<int> SurfaceIndexes;
TArray<int> UniformIndexes;
TArray<SurfaceUniforms> Uniforms;
TArray<FMaterialState> Materials;
} Mesh;
std::unique_ptr<TriangleMeshShape> Collision;
TArray<LevelSubmeshDrawRange> DrawList;
TArray<LevelSubmeshDrawRange> PortalList;
// Lightmap atlas
int LMTextureCount = 0;
int LMTextureSize = 0;
int LMTextureSize = 1024;
TArray<uint16_t> LMTextureData;
uint16_t LightmapSampleDistance = 16;
TArray<LightmapTile> LightmapTiles;
uint32_t AtlasPixelCount() const { return uint32_t(LMTextureCount * LMTextureSize * LMTextureSize); }
void UpdateCollision();
void GatherSurfacePixelStats(LevelMeshSurfaceStats& stats);
void GatherTilePixelStats(LevelMeshTileStats& stats);
void BuildTileSurfaceLists();
private:
FVector2 ToUV(const FVector3& vert, const LevelMeshSurface* targetSurface);
FVector2 ToUV(const FVector3& vert, const LightmapTile* tile);
};
class LevelMesh
{
public:
LevelMesh();
virtual ~LevelMesh() = default;
std::unique_ptr<LevelSubmesh> StaticMesh = std::make_unique<LevelSubmesh>();
@ -220,9 +79,21 @@ public:
LevelMeshSurface* Trace(const FVector3& start, FVector3 direction, float maxDist);
LevelMeshSurfaceStats GatherSurfacePixelStats();
LevelMeshTileStats GatherTilePixelStats();
// Map defaults
FVector3 SunDirection = FVector3(0.0f, 0.0f, -1.0f);
FVector3 SunColor = FVector3(0.0f, 0.0f, 0.0f);
TArray<LevelMeshPortal> Portals;
};
struct LevelMeshTileStats
{
struct Stats
{
uint32_t total = 0, dirty = 0;
};
Stats tiles, pixels;
};

View file

@ -0,0 +1,18 @@
#pragma once
#include "framework/vectors.h"
class LevelMeshLight
{
public:
FVector3 Origin;
FVector3 RelativeOrigin;
float Radius;
float Intensity;
float InnerAngleCos;
float OuterAngleCos;
FVector3 SpotDir;
FVector3 Color;
int SectorGroup;
};

View file

@ -0,0 +1,72 @@
#pragma once
#include <cstring>
#include "framework/vectors.h"
#include "framework/matrix.h"
struct LevelMeshPortal
{
LevelMeshPortal() { transformation.loadIdentity(); }
VSMatrix transformation;
int sourceSectorGroup = 0;
int targetSectorGroup = 0;
inline FVector3 TransformPosition(const FVector3& pos) const
{
auto v = transformation * FVector4(pos, 1.0);
return FVector3(v.X, v.Y, v.Z);
}
inline FVector3 TransformRotation(const FVector3& dir) const
{
auto v = transformation * FVector4(dir, 0.0);
return FVector3(v.X, v.Y, v.Z);
}
// Checks only transformation
inline bool IsInverseTransformationPortal(const LevelMeshPortal& portal) const
{
auto diff = portal.TransformPosition(TransformPosition(FVector3(0, 0, 0)));
return abs(diff.X) < 0.001 && abs(diff.Y) < 0.001 && abs(diff.Z) < 0.001;
}
// Checks only transformation
inline bool IsEqualTransformationPortal(const LevelMeshPortal& portal) const
{
auto diff = portal.TransformPosition(FVector3(0, 0, 0)) - TransformPosition(FVector3(0, 0, 0));
return (abs(diff.X) < 0.001 && abs(diff.Y) < 0.001 && abs(diff.Z) < 0.001);
}
// Checks transformation, source and destiantion sector groups
inline bool IsEqualPortal(const LevelMeshPortal& portal) const
{
return sourceSectorGroup == portal.sourceSectorGroup && targetSectorGroup == portal.targetSectorGroup && IsEqualTransformationPortal(portal);
}
// Checks transformation, source and destiantion sector groups
inline bool IsInversePortal(const LevelMeshPortal& portal) const
{
return sourceSectorGroup == portal.targetSectorGroup && targetSectorGroup == portal.sourceSectorGroup && IsInverseTransformationPortal(portal);
}
};
// for use with std::set to recursively go through portals and skip returning portals
struct RecursivePortalComparator
{
bool operator()(const LevelMeshPortal& a, const LevelMeshPortal& b) const
{
return !a.IsInversePortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(VSMatrix)) < 0;
}
};
// for use with std::map to reject portals which have the same effect for light rays
struct IdenticalPortalComparator
{
bool operator()(const LevelMeshPortal& a, const LevelMeshPortal& b) const
{
return !a.IsEqualPortal(b) && std::memcmp(&a.transformation, &b.transformation, sizeof(VSMatrix)) < 0;
}
};

View file

@ -0,0 +1,45 @@
#pragma once
#include "framework/tarray.h"
#include "framework/vectors.h"
#include "framework/matrix.h"
#include "framework/bounds.h"
#include "framework/textureid.h"
class LevelSubmesh;
struct LevelMeshSurface;
struct LevelMeshSurface
{
LevelSubmesh* Submesh = nullptr;
struct
{
unsigned int StartVertIndex = 0;
int NumVerts = 0;
unsigned int StartElementIndex = 0;
unsigned int NumElements = 0;
} MeshLocation;
BBox Bounds;
FVector4 Plane = FVector4(0.0f, 0.0f, 1.0f, 0.0f);
int LightmapTileIndex = -1;
bool AlwaysUpdate = false;
FTextureID Texture = FNullTextureID();
float Alpha = 1.0;
bool IsSky = false;
int PortalIndex = 0;
int SectorGroup = 0;
// Light list location in the lightmapper GPU buffers
struct
{
int Pos = -1;
int Count = 0;
int ResetCounter = -1;
} LightList;
};

View file

@ -0,0 +1,72 @@
#pragma once
#include "framework/tarray.h"
#include "framework/vectors.h"
#include "framework/bounds.h"
struct LevelMeshSurface;
struct LightmapTileBinding
{
uint32_t Type = 0;
uint32_t TypeIndex = 0;
uint32_t ControlSector = 0xffffffff;
bool operator<(const LightmapTileBinding& other) const
{
if (TypeIndex != other.TypeIndex) return TypeIndex < other.TypeIndex;
if (ControlSector != other.ControlSector) return ControlSector < other.ControlSector;
return Type < other.Type;
}
};
struct LightmapTile
{
// Surface location in lightmap texture
struct
{
int X = 0;
int Y = 0;
int Width = 0;
int Height = 0;
int ArrayIndex = 0;
uint32_t Area() const { return Width * Height; }
} AtlasLocation;
// Calculate world coordinates to UV coordinates
struct
{
FVector3 TranslateWorldToLocal = { 0.0f, 0.0f, 0.0f };
FVector3 ProjLocalToU = { 0.0f, 0.0f, 0.0f };
FVector3 ProjLocalToV = { 0.0f, 0.0f, 0.0f };
} Transform;
LightmapTileBinding Binding;
// Surfaces that are visible within the lightmap tile
TArray<LevelMeshSurface*> Surfaces;
BBox Bounds;
uint16_t SampleDimension = 0;
FVector4 Plane = FVector4(0.0f, 0.0f, 1.0f, 0.0f);
// True if the tile needs to be rendered into the lightmap texture before it can be used
bool NeedsUpdate = true;
FVector2 ToUV(const FVector3& vert) const
{
FVector3 localPos = vert - Transform.TranslateWorldToLocal;
float u = (1.0f + (localPos | Transform.ProjLocalToU)) / (AtlasLocation.Width + 2);
float v = (1.0f + (localPos | Transform.ProjLocalToV)) / (AtlasLocation.Height + 2);
return FVector2(u, v);
}
FVector2 ToUV(const FVector3& vert, float textureSize) const
{
FVector3 localPos = vert - Transform.TranslateWorldToLocal;
float u = (AtlasLocation.X + (localPos | Transform.ProjLocalToU)) / textureSize;
float v = (AtlasLocation.Y + (localPos | Transform.ProjLocalToV)) / textureSize;
return FVector2(u, v);
}
};

View file

@ -0,0 +1,37 @@
#pragma once
enum
{
CLAMP_NONE = 0,
CLAMP_X,
CLAMP_Y,
CLAMP_XY,
CLAMP_XY_NOMIP,
CLAMP_NOFILTER,
CLAMP_NOFILTER_X,
CLAMP_NOFILTER_Y,
CLAMP_NOFILTER_XY,
CLAMP_CAMTEX,
NUMSAMPLERS
};
class FMaterial;
struct FMaterialState
{
FMaterial* mMaterial = nullptr;
int mClampMode;
int mTranslation;
int mOverrideShader;
bool mChanged;
void Reset()
{
mMaterial = nullptr;
mTranslation = 0;
mClampMode = CLAMP_NONE;
mOverrideShader = -1;
mChanged = false;
}
};

View file

@ -0,0 +1,49 @@
#pragma once
#include "framework/vectors.h"
struct SurfaceUniforms
{
FVector4 uObjectColor;
FVector4 uObjectColor2;
FVector4 uDynLightColor;
FVector4 uAddColor;
FVector4 uTextureAddColor;
FVector4 uTextureModulateColor;
FVector4 uTextureBlendColor;
FVector4 uFogColor;
float uDesaturationFactor; // HWDrawInfo::SetColor
float uInterpolationFactor;
float timer;
int useVertexData;
FVector4 uVertexColor; // HWDrawInfo::SetColor
FVector4 uVertexNormal;
FVector4 uGlowTopPlane;
FVector4 uGlowTopColor;
FVector4 uGlowBottomPlane;
FVector4 uGlowBottomColor;
FVector4 uGradientTopPlane;
FVector4 uGradientBottomPlane;
FVector4 uSplitTopPlane;
FVector4 uSplitBottomPlane;
FVector4 uDetailParms;
FVector4 uNpotEmulation;
FVector2 uClipSplit;
FVector2 uSpecularMaterial;
float uLightLevel; // HWDrawInfo::SetColor
float uFogDensity;
float uLightFactor;
float uLightDist;
float uAlphaThreshold;
int uTextureIndex;
float padding2;
float padding3;
};

View file

@ -20,37 +20,36 @@
**
*/
#include "vk_raytrace.h"
#include "vk_levelmesh.h"
#include "zvulkan/vulkanbuilders.h"
#include "vk_renderdevice.h"
#include "hw_levelmesh.h"
//#include "hw_material.h"
//#include "texturemanager.h"
VkRaytrace::VkRaytrace(VulkanRenderDevice* fb) : fb(fb)
VkLevelMesh::VkLevelMesh(VulkanRenderDevice* fb) : fb(fb)
{
useRayQuery = fb->GetDevice()->SupportsExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME) && fb->GetDevice()->PhysicalDevice.Features.RayQuery.rayQuery;
SetLevelMesh(nullptr);
}
void VkRaytrace::SetLevelMesh(LevelMesh* mesh)
void VkLevelMesh::SetLevelMesh(LevelMesh* mesh)
{
if (!mesh)
mesh = &NullMesh;
Reset();
Mesh = mesh;
CreateVulkanObjects();
}
void VkRaytrace::Reset()
void VkLevelMesh::Reset()
{
auto deletelist = fb->GetCommands()->DrawDeleteList.get();
deletelist->Add(std::move(VertexBuffer));
deletelist->Add(std::move(UniformIndexBuffer));
deletelist->Add(std::move(IndexBuffer));
deletelist->Add(std::move(NodeBuffer));
deletelist->Add(std::move(SurfaceBuffer));
deletelist->Add(std::move(UniformsBuffer));
deletelist->Add(std::move(SurfaceIndexBuffer));
deletelist->Add(std::move(PortalBuffer));
deletelist->Add(std::move(StaticBLAS.ScratchBuffer));
@ -60,301 +59,136 @@ void VkRaytrace::Reset()
deletelist->Add(std::move(DynamicBLAS.AccelStructBuffer));
deletelist->Add(std::move(DynamicBLAS.AccelStruct));
deletelist->Add(std::move(TopLevelAS.TransferBuffer));
deletelist->Add(std::move(TopLevelAS.ScratchBuffer));
deletelist->Add(std::move(TopLevelAS.InstanceBuffer));
deletelist->Add(std::move(TopLevelAS.ScratchBuffer));
deletelist->Add(std::move(TopLevelAS.AccelStructBuffer));
deletelist->Add(std::move(TopLevelAS.AccelStruct));
}
void VkRaytrace::CreateVulkanObjects()
void VkLevelMesh::CreateVulkanObjects()
{
Reset();
CreateBuffers();
UploadMeshes(false);
if (useRayQuery)
{
// Wait for uploads to finish
PipelineBarrier()
.AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
CreateStaticBLAS();
CreateDynamicBLAS();
CreateTLASInstanceBuffer();
UploadTLASInstanceBuffer();
// Wait for bottom level builds to finish before using it as input to a toplevel accel structure. Also wait for the instance buffer upload to complete.
PipelineBarrier()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
CreateTopLevelAS();
// Finish building the accel struct before using it from the shaders
PipelineBarrier()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
else
{
// Uploads must finish before we can read from the shaders
PipelineBarrier()
.AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
}
void VkRaytrace::BeginFrame()
void VkLevelMesh::BeginFrame()
{
UploadMeshes(true);
if (useRayQuery)
{
UpdateDynamicBLAS();
// Wait for uploads to finish
PipelineBarrier()
.AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
// Create a new dynamic BLAS
// To do: we should reuse the buffers. However this requires we know when the command buffers are completely done with them first.
auto deletelist = fb->GetCommands()->DrawDeleteList.get();
deletelist->Add(std::move(DynamicBLAS.ScratchBuffer));
deletelist->Add(std::move(DynamicBLAS.AccelStructBuffer));
deletelist->Add(std::move(DynamicBLAS.AccelStruct));
deletelist->Add(std::move(TopLevelAS.TransferBuffer));
deletelist->Add(std::move(TopLevelAS.InstanceBuffer));
DynamicBLAS = CreateBLAS(Mesh->DynamicMesh.get(), true, Mesh->StaticMesh->Mesh.Vertices.Size(), Mesh->StaticMesh->Mesh.Indexes.Size());
CreateTLASInstanceBuffer();
UploadTLASInstanceBuffer();
// Wait for bottom level builds to finish before using it as input to a toplevel accel structure. Also wait for the instance buffer upload to complete.
PipelineBarrier()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR | VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR | VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
UpdateTopLevelAS();
// Finish building the accel struct before using it from the shaders
PipelineBarrier()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
else
{
// Uploads must finish before we can read from the shaders
PipelineBarrier()
.AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
}
void VkRaytrace::UploadMeshes(bool dynamicOnly)
void VkLevelMesh::UploadMeshes(bool dynamicOnly)
{
TArray<SubmeshBufferLocation> locations(2);
// Find submesh buffer sizes
for (LevelSubmesh* submesh : { Mesh->StaticMesh.get(), Mesh->DynamicMesh.get() })
{
SubmeshBufferLocation location;
location.Submesh = submesh;
location.VertexSize = submesh->MeshVertices.Size();
location.IndexSize = submesh->MeshElements.Size();
location.NodeSize = (int)submesh->Collision->get_nodes().size();
location.SurfaceIndexSize = submesh->MeshSurfaceIndexes.Size();
location.SurfaceSize = submesh->GetSurfaceCount();
locations.Push(location);
}
// Find submesh locations in buffers
for (unsigned int i = 1, count = locations.Size(); i < count; i++)
{
const SubmeshBufferLocation& prev = locations[i - 1];
SubmeshBufferLocation& cur = locations[i];
cur.VertexOffset = prev.VertexOffset + prev.VertexSize;
cur.IndexOffset = prev.IndexOffset + prev.IndexSize;
cur.NodeOffset = prev.NodeOffset + prev.NodeSize;
cur.SurfaceIndexOffset = prev.SurfaceIndexOffset + prev.SurfaceIndexSize;
cur.SurfaceOffset = prev.SurfaceOffset + prev.SurfaceSize;
if (
cur.VertexOffset + cur.VertexSize > GetMaxVertexBufferSize() ||
cur.IndexOffset + cur.IndexSize > GetMaxIndexBufferSize() ||
cur.NodeOffset + cur.NodeSize > GetMaxNodeBufferSize() ||
cur.SurfaceOffset + cur.SurfaceSize > GetMaxSurfaceBufferSize() ||
cur.SurfaceIndexOffset + cur.SurfaceIndexSize > GetMaxSurfaceIndexBufferSize())
{
I_FatalError("Dynamic accel struct buffers are too small!");
}
}
unsigned int start = dynamicOnly;
unsigned int end = locations.Size();
// Figure out how much memory we need to transfer it to the GPU
size_t transferBufferSize = sizeof(CollisionNodeBufferHeader) + sizeof(CollisionNode);
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
transferBufferSize += cur.Submesh->MeshVertices.Size() * sizeof(SurfaceVertex);
transferBufferSize += cur.Submesh->MeshElements.Size() * sizeof(uint32_t);
transferBufferSize += cur.Submesh->Collision->get_nodes().size() * sizeof(CollisionNode);
transferBufferSize += cur.Submesh->MeshSurfaceIndexes.Size() * sizeof(int);
transferBufferSize += cur.Submesh->GetSurfaceCount() * sizeof(SurfaceInfo);
}
if (!dynamicOnly)
transferBufferSize += Mesh->StaticMesh->Portals.Size() * sizeof(PortalInfo);
// Begin the transfer
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
auto transferBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY)
.Size(transferBufferSize)
.DebugName("UploadMeshes")
.Create(fb->GetDevice());
uint8_t* data = (uint8_t*)transferBuffer->Map(0, transferBufferSize);
size_t datapos = 0;
// Copy node buffer header and create a root node that merges the static and dynamic AABB trees
if (locations[1].Submesh->Collision->get_root() != -1)
{
int root0 = locations[0].Submesh->Collision->get_root();
int root1 = locations[1].Submesh->Collision->get_root();
const auto& node0 = locations[0].Submesh->Collision->get_nodes()[root0];
const auto& node1 = locations[1].Submesh->Collision->get_nodes()[root1];
FVector3 aabbMin(std::min(node0.aabb.min.X, node1.aabb.min.X), std::min(node0.aabb.min.Y, node1.aabb.min.Y), std::min(node0.aabb.min.Z, node1.aabb.min.Z));
FVector3 aabbMax(std::max(node0.aabb.max.X, node1.aabb.max.X), std::max(node0.aabb.max.Y, node1.aabb.max.Y), std::max(node0.aabb.max.Z, node1.aabb.max.Z));
CollisionBBox bbox(aabbMin, aabbMax);
CollisionNodeBufferHeader nodesHeader;
nodesHeader.root = locations[1].NodeOffset + locations[1].NodeSize;
CollisionNode info;
info.center = bbox.Center;
info.extents = bbox.Extents;
info.left = locations[0].NodeOffset + root0;
info.right = locations[1].NodeOffset + root1;
info.element_index = -1;
*((CollisionNodeBufferHeader*)(data + datapos)) = nodesHeader;
*((CollisionNode*)(data + datapos + sizeof(CollisionNodeBufferHeader))) = info;
cmdbuffer->copyBuffer(transferBuffer.get(), NodeBuffer.get(), datapos, 0, sizeof(CollisionNodeBufferHeader));
cmdbuffer->copyBuffer(transferBuffer.get(), NodeBuffer.get(), datapos + sizeof(CollisionNodeBufferHeader), sizeof(CollisionNodeBufferHeader) + nodesHeader.root * sizeof(CollisionNode), sizeof(CollisionNode));
}
else // second submesh is empty, just point the header at the first one
{
CollisionNodeBufferHeader nodesHeader;
nodesHeader.root = locations[0].Submesh->Collision->get_root();
*((CollisionNodeBufferHeader*)(data + datapos)) = nodesHeader;
cmdbuffer->copyBuffer(transferBuffer.get(), NodeBuffer.get(), datapos, 0, sizeof(CollisionNodeBufferHeader));
}
datapos += sizeof(CollisionNodeBufferHeader) + sizeof(CollisionNode);
// Copy vertices
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
SurfaceVertex* vertices = (SurfaceVertex*)(data + datapos);
for (int j = 0, count = submesh->MeshVertices.Size(); j < count; ++j)
*(vertices++) = { { submesh->MeshVertices[j], 1.0f }, submesh->MeshVertexUVs[j], float(j), j + 10000.0f };
size_t copysize = submesh->MeshVertices.Size() * sizeof(SurfaceVertex);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), VertexBuffer.get(), datapos, cur.VertexOffset * sizeof(SurfaceVertex), copysize);
datapos += copysize;
}
// Copy indexes
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
uint32_t* indexes = (uint32_t*)(data + datapos);
for (int j = 0, count = submesh->MeshElements.Size(); j < count; ++j)
*(indexes++) = cur.VertexOffset + submesh->MeshElements[j];
size_t copysize = submesh->MeshElements.Size() * sizeof(uint32_t);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), IndexBuffer.get(), datapos, cur.IndexOffset * sizeof(uint32_t), copysize);
datapos += copysize;
}
// Copy collision nodes
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
CollisionNode* nodes = (CollisionNode*)(data + datapos);
for (auto& node : submesh->Collision->get_nodes())
{
CollisionNode info;
info.center = node.aabb.Center;
info.extents = node.aabb.Extents;
info.left = node.left != -1 ? node.left + cur.NodeOffset : -1;
info.right = node.right != -1 ? node.right + cur.NodeOffset : -1;
info.element_index = node.element_index != -1 ? node.element_index + cur.IndexOffset : -1;
*(nodes++) = info;
}
size_t copysize = submesh->Collision->get_nodes().size() * sizeof(CollisionNode);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), NodeBuffer.get(), datapos, +sizeof(CollisionNodeBufferHeader) + cur.NodeOffset * sizeof(CollisionNode), copysize);
datapos += copysize;
}
// Copy surface indexes
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
int* indexes = (int*)(data + datapos);
for (int j = 0, count = submesh->MeshSurfaceIndexes.Size(); j < count; ++j)
*(indexes++) = cur.SurfaceIndexOffset + submesh->MeshSurfaceIndexes[j];
size_t copysize = submesh->MeshSurfaceIndexes.Size() * sizeof(int);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), SurfaceIndexBuffer.get(), datapos, cur.SurfaceIndexOffset * sizeof(int), copysize);
datapos += copysize;
}
// Copy surfaces
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
SurfaceInfo* surfaces = (SurfaceInfo*)(data + datapos);
for (int j = 0, count = submesh->GetSurfaceCount(); j < count; ++j)
{
LevelMeshSurface* surface = submesh->GetSurface(j);
SurfaceInfo info;
info.Normal = surface->plane.XYZ();
info.PortalIndex = surface->portalIndex;
info.SamplingDistance = (float)surface->sampleDimension;
info.Sky = surface->bSky;
if (surface->texture.isValid())
{
info.TextureIndex = fb->GetBindlessTextureIndex(surface->texture);
}
else
{
info.TextureIndex = -1;
}
info.Alpha = surface->alpha;
*(surfaces++) = info;
}
size_t copysize = submesh->GetSurfaceCount() * sizeof(SurfaceInfo);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), SurfaceBuffer.get(), datapos, cur.SurfaceOffset * sizeof(SurfaceInfo), copysize);
datapos += copysize;
}
// Copy portals
if (!dynamicOnly)
{
PortalInfo* portals = (PortalInfo*)(data + datapos);
for (auto& portal : Mesh->StaticMesh->Portals)
{
PortalInfo info;
info.transformation = portal.transformation;
*(portals++) = info;
}
size_t copysize = Mesh->StaticMesh->Portals.Size() * sizeof(PortalInfo);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), PortalBuffer.get(), datapos, 0, copysize);
datapos += copysize;
}
assert(datapos == transferBufferSize);
// End the transfer
transferBuffer->Unmap();
fb->GetCommands()->TransferDeleteList->Add(std::move(transferBuffer));
PipelineBarrier()
.AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, useRayQuery ? VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR : VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, useRayQuery ? VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR : VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
VkLevelMeshUploader uploader(this);
uploader.Upload(dynamicOnly);
}
int VkRaytrace::GetMaxVertexBufferSize()
int VkLevelMesh::GetMaxVertexBufferSize()
{
return Mesh->StaticMesh->MeshVertices.Size() + MaxDynamicVertices;
return Mesh->StaticMesh->Mesh.Vertices.Size() + MaxDynamicVertices;
}
int VkRaytrace::GetMaxIndexBufferSize()
int VkLevelMesh::GetMaxIndexBufferSize()
{
return Mesh->StaticMesh->MeshElements.Size() + MaxDynamicIndexes;
return Mesh->StaticMesh->Mesh.Indexes.Size() + MaxDynamicIndexes;
}
int VkRaytrace::GetMaxNodeBufferSize()
int VkLevelMesh::GetMaxNodeBufferSize()
{
return (int)Mesh->StaticMesh->Collision->get_nodes().size() + MaxDynamicNodes + 1; // + 1 for the merge root node
}
int VkRaytrace::GetMaxSurfaceBufferSize()
int VkLevelMesh::GetMaxSurfaceBufferSize()
{
return Mesh->StaticMesh->GetSurfaceCount() + MaxDynamicSurfaces;
}
int VkRaytrace::GetMaxSurfaceIndexBufferSize()
int VkLevelMesh::GetMaxUniformsBufferSize()
{
return Mesh->StaticMesh->MeshSurfaceIndexes.Size() + MaxDynamicSurfaceIndexes;
return Mesh->StaticMesh->Mesh.Uniforms.Size() + MaxDynamicUniforms;
}
void VkRaytrace::CreateBuffers()
int VkLevelMesh::GetMaxSurfaceIndexBufferSize()
{
return Mesh->StaticMesh->Mesh.SurfaceIndexes.Size() + MaxDynamicSurfaceIndexes;
}
void VkLevelMesh::CreateBuffers()
{
VertexBuffer = BufferBuilder()
.Usage(
@ -364,10 +198,18 @@ void VkRaytrace::CreateBuffers()
VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT |
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR : 0) |
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
.Size(GetMaxVertexBufferSize() * sizeof(SurfaceVertex))
.Size(GetMaxVertexBufferSize() * sizeof(FFlatVertex))
.DebugName("VertexBuffer")
.Create(fb->GetDevice());
UniformIndexBuffer = BufferBuilder()
.Usage(
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(GetMaxVertexBufferSize() * sizeof(int))
.DebugName("UniformIndexes")
.Create(fb->GetDevice());
IndexBuffer = BufferBuilder()
.Usage(
VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
@ -398,14 +240,20 @@ void VkRaytrace::CreateBuffers()
.DebugName("SurfaceBuffer")
.Create(fb->GetDevice());
UniformsBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(GetMaxUniformsBufferSize() * sizeof(SurfaceUniforms))
.DebugName("SurfaceUniformsBuffer")
.Create(fb->GetDevice());
PortalBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
.Size(Mesh->StaticMesh->Portals.Size() * sizeof(PortalInfo))
.Size(Mesh->Portals.Size() * sizeof(PortalInfo))
.DebugName("PortalBuffer")
.Create(fb->GetDevice());
}
VkRaytrace::BLAS VkRaytrace::CreateBLAS(LevelSubmesh* submesh, bool preferFastBuild, int vertexOffset, int indexOffset)
VkLevelMesh::BLAS VkLevelMesh::CreateBLAS(LevelSubmesh* submesh, bool preferFastBuild, int vertexOffset, int indexOffset)
{
BLAS blas;
@ -418,10 +266,10 @@ VkRaytrace::BLAS VkRaytrace::CreateBLAS(LevelSubmesh* submesh, bool preferFastBu
accelStructBLDesc.geometry.triangles = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR };
accelStructBLDesc.geometry.triangles.vertexFormat = VK_FORMAT_R32G32B32A32_SFLOAT;
accelStructBLDesc.geometry.triangles.vertexData.deviceAddress = VertexBuffer->GetDeviceAddress();
accelStructBLDesc.geometry.triangles.vertexStride = sizeof(SurfaceVertex);
accelStructBLDesc.geometry.triangles.vertexStride = sizeof(FFlatVertex);
accelStructBLDesc.geometry.triangles.indexType = VK_INDEX_TYPE_UINT32;
accelStructBLDesc.geometry.triangles.indexData.deviceAddress = IndexBuffer->GetDeviceAddress() + indexOffset * sizeof(uint32_t);
accelStructBLDesc.geometry.triangles.maxVertex = vertexOffset + submesh->MeshVertices.Size() - 1;
accelStructBLDesc.geometry.triangles.maxVertex = vertexOffset + submesh->Mesh.Vertices.Size() - 1;
buildInfo.type = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR;
buildInfo.flags = preferFastBuild ? VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_KHR : VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR;
@ -429,7 +277,7 @@ VkRaytrace::BLAS VkRaytrace::CreateBLAS(LevelSubmesh* submesh, bool preferFastBu
buildInfo.geometryCount = 1;
buildInfo.ppGeometries = geometries;
uint32_t maxPrimitiveCount = submesh->MeshElements.Size() / 3;
uint32_t maxPrimitiveCount = submesh->Mesh.Indexes.Size() / 3;
VkAccelerationStructureBuildSizesInfoKHR sizeInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR };
vkGetAccelerationStructureBuildSizesKHR(fb->GetDevice()->device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &buildInfo, &maxPrimitiveCount, &sizeInfo);
@ -462,48 +310,21 @@ VkRaytrace::BLAS VkRaytrace::CreateBLAS(LevelSubmesh* submesh, bool preferFastBu
fb->GetCommands()->GetTransferCommands()->buildAccelerationStructures(1, &buildInfo, rangeInfos);
// Finish building before using it as input to a toplevel accel structure
PipelineBarrier()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
return blas;
}
void VkRaytrace::CreateStaticBLAS()
void VkLevelMesh::CreateStaticBLAS()
{
StaticBLAS = CreateBLAS(Mesh->StaticMesh.get(), false, 0, 0);
}
void VkRaytrace::CreateDynamicBLAS()
void VkLevelMesh::CreateDynamicBLAS()
{
DynamicBLAS = CreateBLAS(Mesh->DynamicMesh.get(), true, Mesh->StaticMesh->MeshVertices.Size(), Mesh->StaticMesh->MeshElements.Size());
DynamicBLAS = CreateBLAS(Mesh->DynamicMesh.get(), true, Mesh->StaticMesh->Mesh.Vertices.Size(), Mesh->StaticMesh->Mesh.Indexes.Size());
}
void VkRaytrace::CreateTopLevelAS()
void VkLevelMesh::CreateTLASInstanceBuffer()
{
auto deletelist = fb->GetCommands()->DrawDeleteList.get();
deletelist->Add(std::move(TopLevelAS.TransferBuffer));
deletelist->Add(std::move(TopLevelAS.ScratchBuffer));
deletelist->Add(std::move(TopLevelAS.InstanceBuffer));
deletelist->Add(std::move(TopLevelAS.AccelStructBuffer));
deletelist->Add(std::move(TopLevelAS.AccelStruct));
VkAccelerationStructureInstanceKHR instances[2] = {};
instances[0].transform.matrix[0][0] = 1.0f;
instances[0].transform.matrix[1][1] = 1.0f;
instances[0].transform.matrix[2][2] = 1.0f;
instances[0].mask = 0xff;
instances[0].flags = 0;
instances[0].accelerationStructureReference = StaticBLAS.AccelStruct->GetDeviceAddress();
instances[1].transform.matrix[0][0] = 1.0f;
instances[1].transform.matrix[1][1] = 1.0f;
instances[1].transform.matrix[2][2] = 1.0f;
instances[1].mask = 0xff;
instances[1].flags = 0;
instances[1].accelerationStructureReference = DynamicBLAS.AccelStruct->GetDeviceAddress();
TopLevelAS.TransferBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY)
.Size(sizeof(VkAccelerationStructureInstanceKHR) * 2)
@ -515,18 +336,10 @@ void VkRaytrace::CreateTopLevelAS()
.Size(sizeof(VkAccelerationStructureInstanceKHR) * 2)
.DebugName("TopLevelAS.InstanceBuffer")
.Create(fb->GetDevice());
}
auto data = (uint8_t*)TopLevelAS.TransferBuffer->Map(0, sizeof(VkAccelerationStructureInstanceKHR) * 2);
memcpy(data, instances, sizeof(VkAccelerationStructureInstanceKHR) * 2);
TopLevelAS.TransferBuffer->Unmap();
fb->GetCommands()->GetTransferCommands()->copyBuffer(TopLevelAS.TransferBuffer.get(), TopLevelAS.InstanceBuffer.get());
// Finish transfering before using it as input
PipelineBarrier()
.AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
void VkLevelMesh::CreateTopLevelAS()
{
VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR };
VkAccelerationStructureGeometryKHR accelStructTLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR };
VkAccelerationStructureGeometryKHR* geometries[] = { &accelStructTLDesc };
@ -573,53 +386,10 @@ void VkRaytrace::CreateTopLevelAS()
rangeInfo.primitiveCount = 2;
fb->GetCommands()->GetTransferCommands()->buildAccelerationStructures(1, &buildInfo, rangeInfos);
// Finish building the accel struct before using as input in a fragment shader
PipelineBarrier()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
void VkRaytrace::UpdateDynamicBLAS()
void VkLevelMesh::UpdateTopLevelAS()
{
// To do: should we reuse the buffers?
auto deletelist = fb->GetCommands()->DrawDeleteList.get();
deletelist->Add(std::move(DynamicBLAS.ScratchBuffer));
deletelist->Add(std::move(DynamicBLAS.AccelStructBuffer));
deletelist->Add(std::move(DynamicBLAS.AccelStruct));
DynamicBLAS = CreateBLAS(Mesh->DynamicMesh.get(), true, Mesh->StaticMesh->MeshVertices.Size(), Mesh->StaticMesh->MeshElements.Size());
}
void VkRaytrace::UpdateTopLevelAS()
{
VkAccelerationStructureInstanceKHR instances[2] = {};
instances[0].transform.matrix[0][0] = 1.0f;
instances[0].transform.matrix[1][1] = 1.0f;
instances[0].transform.matrix[2][2] = 1.0f;
instances[0].mask = 0xff;
instances[0].flags = 0;
instances[0].accelerationStructureReference = StaticBLAS.AccelStruct->GetDeviceAddress();
instances[1].transform.matrix[0][0] = 1.0f;
instances[1].transform.matrix[1][1] = 1.0f;
instances[1].transform.matrix[2][2] = 1.0f;
instances[1].mask = 0xff;
instances[1].flags = 0;
instances[1].accelerationStructureReference = DynamicBLAS.AccelStruct->GetDeviceAddress();
auto data = (uint8_t*)TopLevelAS.TransferBuffer->Map(0, sizeof(VkAccelerationStructureInstanceKHR) * 2);
memcpy(data, instances, sizeof(VkAccelerationStructureInstanceKHR) * 2);
TopLevelAS.TransferBuffer->Unmap();
fb->GetCommands()->GetTransferCommands()->copyBuffer(TopLevelAS.TransferBuffer.get(), TopLevelAS.InstanceBuffer.get());
// Finish transfering before using it as input
PipelineBarrier()
.AddMemory(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_KHR)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR);
VkAccelerationStructureBuildGeometryInfoKHR buildInfo = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR };
VkAccelerationStructureGeometryKHR accelStructTLDesc = { VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR };
VkAccelerationStructureGeometryKHR* geometries[] = { &accelStructTLDesc };
@ -642,10 +412,373 @@ void VkRaytrace::UpdateTopLevelAS()
rangeInfo.primitiveCount = 2;
fb->GetCommands()->GetTransferCommands()->buildAccelerationStructures(1, &buildInfo, rangeInfos);
// Finish building the accel struct before using as input in a fragment shader
PipelineBarrier()
.AddMemory(VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_KHR, VK_ACCESS_SHADER_READ_BIT)
.Execute(fb->GetCommands()->GetTransferCommands(), VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_KHR, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
void VkLevelMesh::UploadTLASInstanceBuffer()
{
VkAccelerationStructureInstanceKHR instances[2] = {};
instances[0].transform.matrix[0][0] = 1.0f;
instances[0].transform.matrix[1][1] = 1.0f;
instances[0].transform.matrix[2][2] = 1.0f;
instances[0].mask = 0xff;
instances[0].flags = 0;
instances[0].accelerationStructureReference = StaticBLAS.AccelStruct->GetDeviceAddress();
instances[1].transform.matrix[0][0] = 1.0f;
instances[1].transform.matrix[1][1] = 1.0f;
instances[1].transform.matrix[2][2] = 1.0f;
instances[1].mask = 0xff;
instances[1].flags = 0;
instances[1].accelerationStructureReference = DynamicBLAS.AccelStruct->GetDeviceAddress();
auto data = (uint8_t*)TopLevelAS.TransferBuffer->Map(0, sizeof(VkAccelerationStructureInstanceKHR) * 2);
memcpy(data, instances, sizeof(VkAccelerationStructureInstanceKHR) * 2);
TopLevelAS.TransferBuffer->Unmap();
fb->GetCommands()->GetTransferCommands()->copyBuffer(TopLevelAS.TransferBuffer.get(), TopLevelAS.InstanceBuffer.get());
}
/////////////////////////////////////////////////////////////////////////////
VkLevelMeshUploader::VkLevelMeshUploader(VkLevelMesh* mesh) : Mesh(mesh)
{
}
void VkLevelMeshUploader::Upload(bool dynamicOnly)
{
UpdateSizes();
UpdateLocations();
start = dynamicOnly;
end = locations.Size();
size_t transferBufferSize = GetTransferSize();
if (transferBufferSize == 0)
return;
BeginTransfer(transferBufferSize);
UploadNodes();
UploadVertices();
UploadUniformIndexes();
UploadIndexes();
UploadSurfaceIndexes();
UploadSurfaces();
UploadUniforms();
UploadPortals();
EndTransfer(transferBufferSize);
}
void VkLevelMeshUploader::BeginTransfer(size_t transferBufferSize)
{
cmdbuffer = Mesh->fb->GetCommands()->GetTransferCommands();
transferBuffer = BufferBuilder()
.Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY)
.Size(transferBufferSize)
.DebugName("UploadMeshes")
.Create(Mesh->fb->GetDevice());
data = (uint8_t*)transferBuffer->Map(0, transferBufferSize);
datapos = 0;
}
void VkLevelMeshUploader::EndTransfer(size_t transferBufferSize)
{
assert(datapos == transferBufferSize);
transferBuffer->Unmap();
Mesh->fb->GetCommands()->TransferDeleteList->Add(std::move(transferBuffer));
}
void VkLevelMeshUploader::UploadNodes()
{
// Copy node buffer header and create a root node that merges the static and dynamic AABB trees
if (locations[1].Submesh->Collision->get_root() != -1)
{
int root0 = locations[0].Submesh->Collision->get_root();
int root1 = locations[1].Submesh->Collision->get_root();
const auto& node0 = locations[0].Submesh->Collision->get_nodes()[root0];
const auto& node1 = locations[1].Submesh->Collision->get_nodes()[root1];
FVector3 aabbMin(std::min(node0.aabb.min.X, node1.aabb.min.X), std::min(node0.aabb.min.Y, node1.aabb.min.Y), std::min(node0.aabb.min.Z, node1.aabb.min.Z));
FVector3 aabbMax(std::max(node0.aabb.max.X, node1.aabb.max.X), std::max(node0.aabb.max.Y, node1.aabb.max.Y), std::max(node0.aabb.max.Z, node1.aabb.max.Z));
CollisionBBox bbox(aabbMin, aabbMax);
CollisionNodeBufferHeader nodesHeader;
nodesHeader.root = locations[1].Node.Offset + locations[1].Node.Size;
CollisionNode info;
info.center = bbox.Center;
info.extents = bbox.Extents;
info.left = locations[0].Node.Offset + root0;
info.right = locations[1].Node.Offset + root1;
info.element_index = -1;
*((CollisionNodeBufferHeader*)(data + datapos)) = nodesHeader;
*((CollisionNode*)(data + datapos + sizeof(CollisionNodeBufferHeader))) = info;
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->NodeBuffer.get(), datapos, 0, sizeof(CollisionNodeBufferHeader));
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->NodeBuffer.get(), datapos + sizeof(CollisionNodeBufferHeader), sizeof(CollisionNodeBufferHeader) + nodesHeader.root * sizeof(CollisionNode), sizeof(CollisionNode));
}
else // second submesh is empty, just point the header at the first one
{
CollisionNodeBufferHeader nodesHeader;
nodesHeader.root = locations[0].Submesh->Collision->get_root();
*((CollisionNodeBufferHeader*)(data + datapos)) = nodesHeader;
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->NodeBuffer.get(), datapos, 0, sizeof(CollisionNodeBufferHeader));
}
datapos += sizeof(CollisionNodeBufferHeader) + sizeof(CollisionNode);
// Copy collision nodes
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
CollisionNode* nodes = (CollisionNode*)(data + datapos);
for (auto& node : submesh->Collision->get_nodes())
{
CollisionNode info;
info.center = node.aabb.Center;
info.extents = node.aabb.Extents;
info.left = node.left != -1 ? node.left + cur.Node.Offset : -1;
info.right = node.right != -1 ? node.right + cur.Node.Offset : -1;
info.element_index = node.element_index != -1 ? node.element_index + cur.Index.Offset : -1;
*(nodes++) = info;
}
size_t copysize = submesh->Collision->get_nodes().size() * sizeof(CollisionNode);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->NodeBuffer.get(), datapos, +sizeof(CollisionNodeBufferHeader) + cur.Node.Offset * sizeof(CollisionNode), copysize);
datapos += copysize;
}
}
void VkLevelMeshUploader::UploadVertices()
{
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
size_t copysize = submesh->Mesh.Vertices.Size() * sizeof(FFlatVertex);
memcpy(data + datapos, submesh->Mesh.Vertices.Data(), copysize);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->VertexBuffer.get(), datapos, cur.Vertex.Offset * sizeof(FFlatVertex), copysize);
datapos += copysize;
}
}
void VkLevelMeshUploader::UploadUniformIndexes()
{
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
size_t copysize = submesh->Mesh.UniformIndexes.Size() * sizeof(int);
memcpy(data + datapos, submesh->Mesh.UniformIndexes.Data(), copysize);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->UniformIndexBuffer.get(), datapos, cur.UniformIndexes.Offset * sizeof(int), copysize);
datapos += copysize;
}
}
void VkLevelMeshUploader::UploadIndexes()
{
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
uint32_t* indexes = (uint32_t*)(data + datapos);
for (int j = 0, count = submesh->Mesh.Indexes.Size(); j < count; ++j)
*(indexes++) = cur.Vertex.Offset + submesh->Mesh.Indexes[j];
size_t copysize = submesh->Mesh.Indexes.Size() * sizeof(uint32_t);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->IndexBuffer.get(), datapos, cur.Index.Offset * sizeof(uint32_t), copysize);
datapos += copysize;
}
}
void VkLevelMeshUploader::UploadSurfaceIndexes()
{
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
int* indexes = (int*)(data + datapos);
for (int j = 0, count = submesh->Mesh.SurfaceIndexes.Size(); j < count; ++j)
*(indexes++) = cur.SurfaceIndex.Offset + submesh->Mesh.SurfaceIndexes[j];
size_t copysize = submesh->Mesh.SurfaceIndexes.Size() * sizeof(int);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->SurfaceIndexBuffer.get(), datapos, cur.SurfaceIndex.Offset * sizeof(int), copysize);
datapos += copysize;
}
}
void VkLevelMeshUploader::UploadSurfaces()
{
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
SurfaceInfo* surfaces = (SurfaceInfo*)(data + datapos);
for (int j = 0, count = submesh->GetSurfaceCount(); j < count; ++j)
{
LevelMeshSurface* surface = submesh->GetSurface(j);
SurfaceInfo info;
info.Normal = FVector3(surface->Plane.X, surface->Plane.Z, surface->Plane.Y);
info.PortalIndex = surface->PortalIndex;
info.Sky = surface->IsSky;
info.Alpha = surface->Alpha;
if (surface->Texture.isValid())
{
#ifdef NEEDS_PORTING
auto mat = FMaterial::ValidateTexture(surface->Texture, 0);
info.TextureIndex = Mesh->fb->GetBindlessTextureIndex(mat, CLAMP_NONE, 0);
#else
info.TextureIndex = 0;
#endif
}
else
{
info.TextureIndex = 0;
}
*(surfaces++) = info;
}
size_t copysize = submesh->GetSurfaceCount() * sizeof(SurfaceInfo);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->SurfaceBuffer.get(), datapos, cur.Surface.Offset * sizeof(SurfaceInfo), copysize);
datapos += copysize;
}
}
void VkLevelMeshUploader::UploadUniforms()
{
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
auto submesh = cur.Submesh;
for (int j = 0, count = submesh->Mesh.Uniforms.Size(); j < count; j++)
{
auto& surfaceUniforms = submesh->Mesh.Uniforms[j];
auto& material = submesh->Mesh.Materials[j];
if (material.mMaterial)
{
#ifdef NEEDS_PORTING
auto source = material.mMaterial->Source();
surfaceUniforms.uSpecularMaterial = { source->GetGlossiness(), source->GetSpecularLevel() };
surfaceUniforms.uTextureIndex = Mesh->fb->GetBindlessTextureIndex(material.mMaterial, material.mClampMode, material.mTranslation);
#else
surfaceUniforms.uTextureIndex = 0;
#endif
}
else
{
surfaceUniforms.uTextureIndex = 0;
}
}
SurfaceUniforms* uniforms = (SurfaceUniforms*)(data + datapos);
size_t copysize = submesh->Mesh.Uniforms.Size() * sizeof(SurfaceUniforms);
memcpy(uniforms, submesh->Mesh.Uniforms.Data(), copysize);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->UniformsBuffer.get(), datapos, cur.Uniforms.Offset * sizeof(SurfaceUniforms), copysize);
datapos += copysize;
}
}
void VkLevelMeshUploader::UploadPortals()
{
if (start == 0)
{
PortalInfo* portals = (PortalInfo*)(data + datapos);
for (auto& portal : Mesh->Mesh->Portals)
{
PortalInfo info;
info.transformation = portal.transformation;
*(portals++) = info;
}
size_t copysize = Mesh->Mesh->Portals.Size() * sizeof(PortalInfo);
if (copysize > 0)
cmdbuffer->copyBuffer(transferBuffer.get(), Mesh->PortalBuffer.get(), datapos, 0, copysize);
datapos += copysize;
}
}
void VkLevelMeshUploader::UpdateSizes()
{
for (LevelSubmesh* submesh : { Mesh->GetMesh()->StaticMesh.get(), Mesh->GetMesh()->DynamicMesh.get() })
{
SubmeshBufferLocation location;
location.Submesh = submesh;
location.Vertex.Size = submesh->Mesh.Vertices.Size();
location.Index.Size = submesh->Mesh.Indexes.Size();
location.Node.Size = (int)submesh->Collision->get_nodes().size();
location.SurfaceIndex.Size = submesh->Mesh.SurfaceIndexes.Size();
location.Surface.Size = submesh->GetSurfaceCount();
location.UniformIndexes.Size = submesh->Mesh.UniformIndexes.Size();
location.Uniforms.Size = submesh->Mesh.Uniforms.Size();
locations.Push(location);
}
}
void VkLevelMeshUploader::UpdateLocations()
{
for (unsigned int i = 1, count = locations.Size(); i < count; i++)
{
const SubmeshBufferLocation& prev = locations[i - 1];
SubmeshBufferLocation& cur = locations[i];
cur.Vertex.Offset = prev.Vertex.Offset + prev.Vertex.Size;
cur.Index.Offset = prev.Index.Offset + prev.Index.Size;
cur.Node.Offset = prev.Node.Offset + prev.Node.Size;
cur.SurfaceIndex.Offset = prev.SurfaceIndex.Offset + prev.SurfaceIndex.Size;
cur.Surface.Offset = prev.Surface.Offset + prev.Surface.Size;
cur.UniformIndexes.Offset = prev.UniformIndexes.Offset + prev.UniformIndexes.Size;
cur.Uniforms.Offset = prev.Uniforms.Offset + prev.Uniforms.Size;
if (
cur.Vertex.Offset + cur.Vertex.Size > Mesh->GetMaxVertexBufferSize() ||
cur.Index.Offset + cur.Index.Size > Mesh->GetMaxIndexBufferSize() ||
cur.Node.Offset + cur.Node.Size > Mesh->GetMaxNodeBufferSize() ||
cur.SurfaceIndex.Offset + cur.SurfaceIndex.Size > Mesh->GetMaxSurfaceIndexBufferSize() ||
cur.Surface.Offset + cur.Surface.Size > Mesh->GetMaxSurfaceBufferSize() ||
cur.UniformIndexes.Offset + cur.UniformIndexes.Size > Mesh->GetMaxVertexBufferSize() ||
cur.Uniforms.Offset + cur.Uniforms.Size > Mesh->GetMaxUniformsBufferSize())
{
I_FatalError("Dynamic accel struct buffers are too small!");
}
}
}
size_t VkLevelMeshUploader::GetTransferSize()
{
// Figure out how much memory we need to transfer it to the GPU
size_t transferBufferSize = sizeof(CollisionNodeBufferHeader) + sizeof(CollisionNode);
for (unsigned int i = start; i < end; i++)
{
const SubmeshBufferLocation& cur = locations[i];
transferBufferSize += cur.Submesh->Mesh.Vertices.Size() * sizeof(FFlatVertex);
transferBufferSize += cur.Submesh->Mesh.UniformIndexes.Size() * sizeof(int);
transferBufferSize += cur.Submesh->Mesh.Indexes.Size() * sizeof(uint32_t);
transferBufferSize += cur.Submesh->Collision->get_nodes().size() * sizeof(CollisionNode);
transferBufferSize += cur.Submesh->Mesh.SurfaceIndexes.Size() * sizeof(int);
transferBufferSize += cur.Submesh->GetSurfaceCount() * sizeof(SurfaceInfo);
transferBufferSize += cur.Submesh->Mesh.Uniforms.Size() * sizeof(SurfaceUniforms);
}
if (start == 0)
transferBufferSize += Mesh->GetMesh()->Portals.Size() * sizeof(PortalInfo);
return transferBufferSize;
}

View file

@ -31,17 +31,10 @@ struct SurfaceInfo
{
FVector3 Normal;
float Sky;
float SamplingDistance;
uint32_t PortalIndex;
int32_t TextureIndex;
float Alpha;
};
struct SurfaceVertex
{
FVector4 pos;
FVector2 uv;
float Padding1, Padding2;
float Padding;
};
struct PortalInfo
@ -49,37 +42,44 @@ struct PortalInfo
VSMatrix transformation;
};
struct SubmeshBufferRange
{
int Offset = 0;
int Size = 0;
};
struct SubmeshBufferLocation
{
LevelSubmesh* Submesh = nullptr;
int VertexOffset = 0;
int VertexSize = 0;
int IndexOffset = 0;
int IndexSize = 0;
int NodeOffset = 0;
int NodeSize = 0;
int SurfaceIndexOffset = 0;
int SurfaceIndexSize = 0;
int SurfaceOffset = 0;
int SurfaceSize = 0;
SubmeshBufferRange Vertex;
SubmeshBufferRange Index;
SubmeshBufferRange Node;
SubmeshBufferRange SurfaceIndex;
SubmeshBufferRange Surface;
SubmeshBufferRange UniformIndexes;
SubmeshBufferRange Uniforms;
};
class VkRaytrace
class VkLevelMesh
{
public:
VkRaytrace(VulkanRenderDevice* fb);
VkLevelMesh(VulkanRenderDevice* fb);
void SetLevelMesh(LevelMesh* mesh);
void BeginFrame();
VulkanAccelerationStructure* GetAccelStruct() { return TopLevelAS.AccelStruct.get(); }
VulkanBuffer* GetVertexBuffer() { return VertexBuffer.get(); }
VulkanBuffer* GetUniformIndexBuffer() { return UniformIndexBuffer.get(); }
VulkanBuffer* GetIndexBuffer() { return IndexBuffer.get(); }
VulkanBuffer* GetNodeBuffer() { return NodeBuffer.get(); }
VulkanBuffer* GetSurfaceIndexBuffer() { return SurfaceIndexBuffer.get(); }
VulkanBuffer* GetSurfaceBuffer() { return SurfaceBuffer.get(); }
VulkanBuffer* GetUniformsBuffer() { return UniformsBuffer.get(); }
VulkanBuffer* GetPortalBuffer() { return PortalBuffer.get(); }
LevelMesh* GetMesh() { return Mesh; }
private:
struct BLAS
{
@ -93,9 +93,11 @@ private:
void CreateBuffers();
void CreateStaticBLAS();
void CreateDynamicBLAS();
void CreateTLASInstanceBuffer();
void CreateTopLevelAS();
void UploadMeshes(bool dynamicOnly);
void UpdateDynamicBLAS();
void UploadTLASInstanceBuffer();
void UpdateTopLevelAS();
BLAS CreateBLAS(LevelSubmesh *submesh, bool preferFastBuild, int vertexOffset, int indexOffset);
@ -104,6 +106,7 @@ private:
int GetMaxIndexBufferSize();
int GetMaxNodeBufferSize();
int GetMaxSurfaceBufferSize();
int GetMaxUniformsBufferSize();
int GetMaxSurfaceIndexBufferSize();
VulkanRenderDevice* fb = nullptr;
@ -114,17 +117,20 @@ private:
LevelMesh* Mesh = nullptr;
std::unique_ptr<VulkanBuffer> VertexBuffer;
std::unique_ptr<VulkanBuffer> UniformIndexBuffer;
std::unique_ptr<VulkanBuffer> IndexBuffer;
std::unique_ptr<VulkanBuffer> SurfaceIndexBuffer;
std::unique_ptr<VulkanBuffer> SurfaceBuffer;
std::unique_ptr<VulkanBuffer> UniformsBuffer;
std::unique_ptr<VulkanBuffer> PortalBuffer;
std::unique_ptr<VulkanBuffer> NodeBuffer;
TArray<SurfaceVertex> Vertices;
TArray<FFlatVertex> Vertices;
static const int MaxDynamicVertices = 100'000;
static const int MaxDynamicIndexes = 100'000;
static const int MaxDynamicSurfaces = 100'000;
static const int MaxDynamicUniforms = 100'000;
static const int MaxDynamicSurfaceIndexes = 25'000;
static const int MaxDynamicNodes = 10'000;
@ -139,4 +145,38 @@ private:
std::unique_ptr<VulkanBuffer> AccelStructBuffer;
std::unique_ptr<VulkanAccelerationStructure> AccelStruct;
} TopLevelAS;
friend class VkLevelMeshUploader;
};
class VkLevelMeshUploader
{
public:
VkLevelMeshUploader(VkLevelMesh* mesh);
void Upload(bool dynamicOnly);
private:
void BeginTransfer(size_t transferBufferSize);
void EndTransfer(size_t transferBufferSize);
void UploadNodes();
void UploadVertices();
void UploadUniformIndexes();
void UploadIndexes();
void UploadSurfaceIndexes();
void UploadSurfaces();
void UploadUniforms();
void UploadPortals();
void UpdateSizes();
void UpdateLocations();
size_t GetTransferSize();
VkLevelMesh* Mesh;
TArray<SubmeshBufferLocation> locations;
unsigned int start = 0;
unsigned int end = 0;
uint8_t* data = nullptr;
size_t datapos = 0;
VulkanCommandBuffer* cmdbuffer = nullptr;
std::unique_ptr<VulkanBuffer> transferBuffer;
};

View file

@ -1,10 +1,8 @@
#include "vk_lightmap.h"
#include "vk_lightmapper.h"
#include "vk_renderdevice.h"
#include "vk_raytrace.h"
#include "vk_levelmesh.h"
#include "zvulkan/vulkanbuilders.h"
#include "framework/halffloat.h"
#include "framework/zstring.h"
#include <map>
#include "glsl/binding_lightmapper.glsl.h"
@ -22,7 +20,6 @@
#include "glsl/vert_copy.glsl.h"
#include "glsl/vert_raytrace.glsl.h"
#include "glsl/vert_screenquad.glsl.h"
#define USE_DRAWINDIRECT
int lm_background_updates = 8;
@ -33,7 +30,7 @@ bool lm_softshadows = true;
bool lm_sunlight = true;
bool lm_blur = true;
VkLightmap::VkLightmap(VulkanRenderDevice* fb) : fb(fb)
VkLightmapper::VkLightmapper(VulkanRenderDevice* fb) : fb(fb)
{
useRayQuery = fb->GetDevice()->SupportsExtension(VK_KHR_RAY_QUERY_EXTENSION_NAME) && fb->GetDevice()->PhysicalDevice.Features.RayQuery.rayQuery;
@ -60,12 +57,12 @@ VkLightmap::VkLightmap(VulkanRenderDevice* fb) : fb(fb)
}
}
VkLightmap::~VkLightmap()
VkLightmapper::~VkLightmapper()
{
ReleaseResources();
}
void VkLightmap::ReleaseResources()
void VkLightmapper::ReleaseResources()
{
if (lights.Buffer)
lights.Buffer->Unmap();
@ -77,25 +74,25 @@ void VkLightmap::ReleaseResources()
drawindexed.ConstantsBuffer->Unmap();
}
void VkLightmap::SetLevelMesh(LevelMesh* level)
void VkLightmapper::SetLevelMesh(LevelMesh* level)
{
mesh = level;
UpdateAccelStructDescriptors();
}
void VkLightmap::BeginFrame()
void VkLightmapper::BeginFrame()
{
lights.Pos = 0;
lights.ResetCounter++;
drawindexed.Pos = 0;
}
void VkLightmap::Raytrace(const TArray<LevelMeshSurface*>& surfaces)
void VkLightmapper::Raytrace(const TArray<LightmapTile*>& tiles)
{
if (surfaces.Size())
if (mesh && tiles.Size() > 0)
{
SelectSurfaces(surfaces);
if (selectedSurfaces.Size() > 0)
SelectTiles(tiles);
if (selectedTiles.Size() > 0)
{
fb->GetCommands()->PushGroup(fb->GetCommands()->GetTransferCommands(), "lightmap.total");
@ -111,41 +108,41 @@ void VkLightmap::Raytrace(const TArray<LevelMeshSurface*>& surfaces)
}
}
void VkLightmap::SelectSurfaces(const TArray<LevelMeshSurface*>& surfaces)
void VkLightmapper::SelectTiles(const TArray<LightmapTile*>& tiles)
{
bakeImage.maxX = 0;
bakeImage.maxY = 0;
selectedSurfaces.Clear();
selectedTiles.Clear();
const int spacing = 5; // Note: the spacing is here to avoid that the resolve sampler finds data from other surface tiles
RectPacker packer(bakeImageSize - spacing, bakeImageSize - spacing, RectPacker::Spacing(spacing));
for (int i = 0, count = surfaces.Size(); i < count; i++)
for (int i = 0, count = tiles.Size(); i < count; i++)
{
LevelMeshSurface* surface = surfaces[i];
LightmapTile* tile = tiles[i];
if (!surface->needsUpdate)
if (!tile->NeedsUpdate)
continue;
// Only grab surfaces until our bake texture is full
auto result = packer.insert(surface->AtlasTile.Width + 2, surface->AtlasTile.Height + 2);
auto result = packer.insert(tile->AtlasLocation.Width + 2, tile->AtlasLocation.Height + 2);
if (result.pageIndex == 0)
{
SelectedSurface selected;
selected.Surface = surface;
SelectedTile selected;
selected.Tile = tile;
selected.X = result.pos.x + 1;
selected.Y = result.pos.y + 1;
selectedSurfaces.Push(selected);
selectedTiles.Push(selected);
bakeImage.maxX = std::max<uint16_t>(bakeImage.maxX, uint16_t(selected.X + surface->AtlasTile.Width + spacing));
bakeImage.maxY = std::max<uint16_t>(bakeImage.maxY, uint16_t(selected.Y + surface->AtlasTile.Height + spacing));
bakeImage.maxX = std::max<uint16_t>(bakeImage.maxX, uint16_t(selected.X + tile->AtlasLocation.Width + spacing));
bakeImage.maxY = std::max<uint16_t>(bakeImage.maxY, uint16_t(selected.Y + tile->AtlasLocation.Height + spacing));
surface->needsUpdate = false;
tile->NeedsUpdate = false;
}
}
}
void VkLightmap::Render()
void VkLightmapper::Render()
{
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
@ -159,12 +156,12 @@ void VkLightmap::Render()
.Execute(cmdbuffer);
VkDeviceSize offset = 0;
cmdbuffer->bindVertexBuffers(0, 1, &fb->GetRaytrace()->GetVertexBuffer()->buffer, &offset);
cmdbuffer->bindIndexBuffer(fb->GetRaytrace()->GetIndexBuffer()->buffer, 0, VK_INDEX_TYPE_UINT32);
cmdbuffer->bindVertexBuffers(0, 1, &fb->GetLevelMesh()->GetVertexBuffer()->buffer, &offset);
cmdbuffer->bindIndexBuffer(fb->GetLevelMesh()->GetIndexBuffer()->buffer, 0, VK_INDEX_TYPE_UINT32);
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipeline[GetRaytracePipelineIndex()].get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 0, raytrace.descriptorSet0.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 1, raytrace.descriptorSet1.get());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 2, fb->GetDescriptorSetManager()->GetBindlessDescriptorSet());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, raytrace.pipelineLayout.get(), 2, fb->GetDescriptorSetManager()->GetBindlessSet());
VkViewport viewport = {};
viewport.maxDepth = 1;
@ -172,35 +169,40 @@ void VkLightmap::Render()
viewport.height = (float)bakeImageSize;
cmdbuffer->setViewport(0, 1, &viewport);
for (int i = 0, count = selectedSurfaces.Size(); i < count; i++)
{
auto& selectedSurface = selectedSurfaces[i];
LevelMeshSurface* targetSurface = selectedSurface.Surface;
int dynamicSurfaceIndexOffset = mesh->StaticMesh->GetSurfaceCount();
int dynamicFirstIndexOffset = mesh->StaticMesh->Mesh.Indexes.Size();
LevelSubmesh* staticMesh = mesh->StaticMesh.get();
int surfaceIndexOffset = 0;
int firstIndexOffset = 0;
if (targetSurface->Submesh != mesh->StaticMesh.get())
{
surfaceIndexOffset = mesh->StaticMesh->GetSurfaceCount();
firstIndexOffset = mesh->StaticMesh->MeshElements.Size();
}
for (int i = 0, count = selectedTiles.Size(); i < count; i++)
{
auto& selectedTile = selectedTiles[i];
LightmapTile* targetTile = selectedTile.Tile;
LightmapRaytracePC pc;
pc.TileX = (float)selectedSurface.X;
pc.TileY = (float)selectedSurface.Y;
pc.SurfaceIndex = surfaceIndexOffset + targetSurface->Submesh->GetSurfaceIndex(targetSurface);
pc.TileX = (float)selectedTile.X;
pc.TileY = (float)selectedTile.Y;
pc.TextureSize = (float)bakeImageSize;
pc.TileWidth = (float)targetSurface->AtlasTile.Width;
pc.TileHeight = (float)targetSurface->AtlasTile.Height;
pc.WorldToLocal = targetSurface->translateWorldToLocal;
pc.ProjLocalToU = targetSurface->projLocalToU;
pc.ProjLocalToV = targetSurface->projLocalToV;
pc.TileWidth = (float)targetTile->AtlasLocation.Width;
pc.TileHeight = (float)targetTile->AtlasLocation.Height;
pc.WorldToLocal = SwapYZ(targetTile->Transform.TranslateWorldToLocal);
pc.ProjLocalToU = SwapYZ(targetTile->Transform.ProjLocalToU);
pc.ProjLocalToV = SwapYZ(targetTile->Transform.ProjLocalToV);
bool buffersFull = false;
// Paint all surfaces visible in the tile
for (LevelMeshSurface* surface : targetSurface->tileSurfaces)
for (LevelMeshSurface* surface : targetTile->Surfaces)
{
int surfaceIndexOffset = 0;
int firstIndexOffset = 0;
if (surface->Submesh != staticMesh)
{
surfaceIndexOffset = dynamicSurfaceIndexOffset;
firstIndexOffset = dynamicFirstIndexOffset;
}
pc.SurfaceIndex = surfaceIndexOffset + surface->Submesh->GetSurfaceIndex(surface);
if (surface->LightList.ResetCounter != lights.ResetCounter)
{
int lightCount = mesh->AddSurfaceLights(surface, templightlist.Data(), (int)templightlist.Size());
@ -220,13 +222,13 @@ void VkLightmap::Render()
for (int i = 0; i < lightCount; i++)
{
const LevelMeshLight* light = &templightlist[i];
lightinfo->Origin = light->Origin;
lightinfo->RelativeOrigin = light->RelativeOrigin;
lightinfo->Origin = SwapYZ(light->Origin);
lightinfo->RelativeOrigin = SwapYZ(light->RelativeOrigin);
lightinfo->Radius = light->Radius;
lightinfo->Intensity = light->Intensity;
lightinfo->InnerAngleCos = light->InnerAngleCos;
lightinfo->OuterAngleCos = light->OuterAngleCos;
lightinfo->SpotDir = light->SpotDir;
lightinfo->SpotDir = SwapYZ(light->SpotDir);
lightinfo->Color = light->Color;
lightinfo++;
}
@ -239,9 +241,9 @@ void VkLightmap::Render()
#ifdef USE_DRAWINDIRECT
VkDrawIndexedIndirectCommand cmd;
cmd.indexCount = surface->numElements;
cmd.indexCount = surface->MeshLocation.NumElements;
cmd.instanceCount = 1;
cmd.firstIndex = firstIndexOffset + surface->startElementIndex;
cmd.firstIndex = firstIndexOffset + surface->MeshLocation.StartElementIndex;
cmd.vertexOffset = 0;
cmd.firstInstance = drawindexed.Pos;
drawindexed.Constants[drawindexed.Pos] = pc;
@ -264,13 +266,13 @@ void VkLightmap::Render()
{
while (i < count)
{
selectedSurfaces[i].Surface->needsUpdate = true;
selectedTiles[i].Tile->NeedsUpdate = true;
i++;
}
break;
}
selectedSurface.Rendered = true;
selectedTile.Rendered = true;
}
#ifdef USE_DRAWINDIRECT
@ -282,10 +284,10 @@ void VkLightmap::Render()
fb->GetCommands()->PopGroup(cmdbuffer);
}
void VkLightmap::UploadUniforms()
void VkLightmapper::UploadUniforms()
{
Uniforms values = {};
values.SunDir = mesh->SunDirection;
values.SunDir = SwapYZ(mesh->SunDirection);
values.SunColor = mesh->SunColor;
values.SunIntensity = 1.0f;
@ -300,7 +302,7 @@ void VkLightmap::UploadUniforms()
.Execute(cmdbuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
}
void VkLightmap::Resolve()
void VkLightmapper::Resolve()
{
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
@ -332,7 +334,7 @@ void VkLightmap::Resolve()
fb->GetCommands()->PopGroup(cmdbuffer);
}
void VkLightmap::Blur()
void VkLightmapper::Blur()
{
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
@ -393,24 +395,24 @@ void VkLightmap::Blur()
fb->GetCommands()->PopGroup(cmdbuffer);
}
void VkLightmap::CopyResult()
void VkLightmapper::CopyResult()
{
// Sort by destination
uint32_t pixels = 0;
for (auto& list : copylists) list.Clear();
for (int i = 0, count = selectedSurfaces.Size(); i < count; i++)
for (int i = 0, count = selectedTiles.Size(); i < count; i++)
{
auto& selected = selectedSurfaces[i];
auto& selected = selectedTiles[i];
if (selected.Rendered)
{
unsigned int pageIndex = (unsigned int)selected.Surface->AtlasTile.ArrayIndex;
unsigned int pageIndex = (unsigned int)selected.Tile->AtlasLocation.ArrayIndex;
if (pageIndex >= copylists.Size())
{
copylists.Resize(pageIndex + 1);
}
copylists[pageIndex].Push(&selected);
pixels += selected.Surface->Area();
pixels += selected.Tile->AtlasLocation.Area();
}
}
@ -473,17 +475,17 @@ void VkLightmap::CopyResult()
// Copy the tile positions into a storage buffer for the vertex shader to read
start = pos;
for (SelectedSurface* selected : list)
for (SelectedTile* selected : list)
{
LevelMeshSurface* surface = selected->Surface;
LightmapTile* tile = selected->Tile;
CopyTileInfo* copyinfo = &copytiles.Tiles[pos++];
copyinfo->SrcPosX = selected->X;
copyinfo->SrcPosY = selected->Y;
copyinfo->DestPosX = surface->AtlasTile.X;
copyinfo->DestPosY = surface->AtlasTile.Y;
copyinfo->TileWidth = surface->AtlasTile.Width;
copyinfo->TileHeight = surface->AtlasTile.Height;
copyinfo->DestPosX = tile->AtlasLocation.X;
copyinfo->DestPosY = tile->AtlasLocation.Y;
copyinfo->TileWidth = tile->AtlasLocation.Width;
copyinfo->TileHeight = tile->AtlasLocation.Height;
}
// Draw the tiles. One instance per tile.
@ -524,7 +526,7 @@ void VkLightmap::CopyResult()
fb->GetCommands()->PopGroup(cmdbuffer);
}
void VkLightmap::CreateShaders()
void VkLightmapper::CreateShaders()
{
std::string prefix = "#version 460\r\n";
std::string traceprefix = "#version 460\r\n";
@ -551,8 +553,8 @@ void VkLightmap::CreateShaders()
.AddSource("vert_raytrace.glsl", LoadPrivateShaderLump("shaders/lightmap/vert_raytrace.glsl").GetChars())
.OnIncludeLocal(onIncludeLocal)
.OnIncludeSystem(onIncludeSystem)
.DebugName("VkLightmap.VertRaytrace")
.Create("VkLightmap.VertRaytrace", fb->GetDevice());
.DebugName("VkLightmapper.VertRaytrace")
.Create("VkLightmapper.VertRaytrace", fb->GetDevice());
shaders.vertScreenquad = ShaderBuilder()
.Type(ShaderType::Vertex)
@ -560,8 +562,8 @@ void VkLightmap::CreateShaders()
.AddSource("vert_screenquad.glsl", LoadPrivateShaderLump("shaders/lightmap/vert_screenquad.glsl").GetChars())
.OnIncludeLocal(onIncludeLocal)
.OnIncludeSystem(onIncludeSystem)
.DebugName("VkLightmap.VertScreenquad")
.Create("VkLightmap.VertScreenquad", fb->GetDevice());
.DebugName("VkLightmapper.VertScreenquad")
.Create("VkLightmapper.VertScreenquad", fb->GetDevice());
shaders.vertCopy = ShaderBuilder()
.Type(ShaderType::Vertex)
@ -569,8 +571,8 @@ void VkLightmap::CreateShaders()
.AddSource("vert_copy.glsl", LoadPrivateShaderLump("shaders/lightmap/vert_copy.glsl").GetChars())
.OnIncludeLocal(onIncludeLocal)
.OnIncludeSystem(onIncludeSystem)
.DebugName("VkLightmap.VertCopy")
.Create("VkLightmap.VertCopy", fb->GetDevice());
.DebugName("VkLightmapper.VertCopy")
.Create("VkLightmapper.VertCopy", fb->GetDevice());
for (int i = 0; i < 8; i++)
{
@ -588,8 +590,8 @@ void VkLightmap::CreateShaders()
.AddSource("frag_raytrace.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_raytrace.glsl").GetChars())
.OnIncludeLocal(onIncludeLocal)
.OnIncludeSystem(onIncludeSystem)
.DebugName("VkLightmap.FragRaytrace")
.Create("VkLightmap.FragRaytrace", fb->GetDevice());
.DebugName("VkLightmapper.FragRaytrace")
.Create("VkLightmapper.FragRaytrace", fb->GetDevice());
}
shaders.fragResolve = ShaderBuilder()
@ -598,8 +600,8 @@ void VkLightmap::CreateShaders()
.AddSource("frag_resolve.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_resolve.glsl").GetChars())
.OnIncludeLocal(onIncludeLocal)
.OnIncludeSystem(onIncludeSystem)
.DebugName("VkLightmap.FragResolve")
.Create("VkLightmap.FragResolve", fb->GetDevice());
.DebugName("VkLightmapper.FragResolve")
.Create("VkLightmapper.FragResolve", fb->GetDevice());
shaders.fragBlur[0] = ShaderBuilder()
.Type(ShaderType::Fragment)
@ -607,8 +609,8 @@ void VkLightmap::CreateShaders()
.AddSource("frag_blur.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_blur.glsl").GetChars())
.OnIncludeLocal(onIncludeLocal)
.OnIncludeSystem(onIncludeSystem)
.DebugName("VkLightmap.FragBlur")
.Create("VkLightmap.FragBlur", fb->GetDevice());
.DebugName("VkLightmapper.FragBlur")
.Create("VkLightmapper.FragBlur", fb->GetDevice());
shaders.fragBlur[1] = ShaderBuilder()
.Type(ShaderType::Fragment)
@ -616,8 +618,8 @@ void VkLightmap::CreateShaders()
.AddSource("frag_blur.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_blur.glsl").GetChars())
.OnIncludeLocal(onIncludeLocal)
.OnIncludeSystem(onIncludeSystem)
.DebugName("VkLightmap.FragBlur")
.Create("VkLightmap.FragBlur", fb->GetDevice());
.DebugName("VkLightmapper.FragBlur")
.Create("VkLightmapper.FragBlur", fb->GetDevice());
shaders.fragCopy = ShaderBuilder()
.Type(ShaderType::Fragment)
@ -625,11 +627,11 @@ void VkLightmap::CreateShaders()
.AddSource("frag_copy.glsl", LoadPrivateShaderLump("shaders/lightmap/frag_copy.glsl").GetChars())
.OnIncludeLocal(onIncludeLocal)
.OnIncludeSystem(onIncludeSystem)
.DebugName("VkLightmap.FragCopy")
.Create("VkLightmap.FragCopy", fb->GetDevice());
.DebugName("VkLightmapper.FragCopy")
.Create("VkLightmapper.FragCopy", fb->GetDevice());
}
int VkLightmap::GetRaytracePipelineIndex()
int VkLightmapper::GetRaytracePipelineIndex()
{
int index = 0;
if (lm_softshadows && useRayQuery)
@ -641,7 +643,7 @@ int VkLightmap::GetRaytracePipelineIndex()
return index;
}
FString VkLightmap::LoadPrivateShaderLump(const char* lumpname)
FString VkLightmapper::LoadPrivateShaderLump(const char* lumpname)
{
static std::map<FString, FString> sources =
{
@ -669,7 +671,12 @@ FString VkLightmap::LoadPrivateShaderLump(const char* lumpname)
return FString();
}
ShaderIncludeResult VkLightmap::OnInclude(FString headerName, FString includerName, size_t depth, bool system)
FString VkLightmapper::LoadPublicShaderLump(const char* lumpname)
{
return LoadPrivateShaderLump(lumpname);
}
ShaderIncludeResult VkLightmapper::OnInclude(FString headerName, FString includerName, size_t depth, bool system)
{
if (depth > 8)
{
@ -695,7 +702,7 @@ ShaderIncludeResult VkLightmap::OnInclude(FString headerName, FString includerNa
return ShaderIncludeResult(headerName.GetChars(), code.GetChars());
}
void VkLightmap::CreateRaytracePipeline()
void VkLightmapper::CreateRaytracePipeline()
{
raytrace.descriptorSetLayout0 = DescriptorSetLayoutBuilder()
.AddBinding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT)
@ -731,7 +738,7 @@ void VkLightmap::CreateRaytracePipeline()
raytrace.pipelineLayout = PipelineLayoutBuilder()
.AddSetLayout(raytrace.descriptorSetLayout0.get())
.AddSetLayout(raytrace.descriptorSetLayout1.get())
.AddSetLayout(fb->GetDescriptorSetManager()->GetBindlessSetLayout())
.AddSetLayout(fb->GetDescriptorSetManager()->GetBindlessLayout())
#ifndef USE_DRAWINDIRECT
.AddPushConstantRange(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(LightmapRaytracePC))
#endif
@ -763,7 +770,7 @@ void VkLightmap::CreateRaytracePipeline()
.RenderPass(raytrace.renderPass.get())
.AddVertexShader(shaders.vertRaytrace.get())
.AddFragmentShader(shaders.fragRaytrace[i].get())
.AddVertexBufferBinding(0, sizeof(SurfaceVertex))
.AddVertexBufferBinding(0, sizeof(FFlatVertex))
.AddVertexAttribute(0, 0, VK_FORMAT_R32G32B32A32_SFLOAT, 0)
.Topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST)
.AddDynamicState(VK_DYNAMIC_STATE_VIEWPORT)
@ -806,38 +813,38 @@ void VkLightmap::CreateRaytracePipeline()
raytrace.descriptorSet1->SetDebugName("raytrace.descriptorSet1");
}
void VkLightmap::UpdateAccelStructDescriptors()
void VkLightmapper::UpdateAccelStructDescriptors()
{
if (useRayQuery)
{
WriteDescriptors()
.AddAccelerationStructure(raytrace.descriptorSet1.get(), 0, fb->GetRaytrace()->GetAccelStruct())
.AddBuffer(raytrace.descriptorSet1.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetRaytrace()->GetVertexBuffer())
.AddBuffer(raytrace.descriptorSet1.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetRaytrace()->GetIndexBuffer())
.AddAccelerationStructure(raytrace.descriptorSet1.get(), 0, fb->GetLevelMesh()->GetAccelStruct())
.AddBuffer(raytrace.descriptorSet1.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetVertexBuffer())
.AddBuffer(raytrace.descriptorSet1.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetIndexBuffer())
.Execute(fb->GetDevice());
}
else
{
WriteDescriptors()
.AddBuffer(raytrace.descriptorSet1.get(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetRaytrace()->GetNodeBuffer())
.AddBuffer(raytrace.descriptorSet1.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetRaytrace()->GetVertexBuffer())
.AddBuffer(raytrace.descriptorSet1.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetRaytrace()->GetIndexBuffer())
.AddBuffer(raytrace.descriptorSet1.get(), 0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetNodeBuffer())
.AddBuffer(raytrace.descriptorSet1.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetVertexBuffer())
.AddBuffer(raytrace.descriptorSet1.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetIndexBuffer())
.Execute(fb->GetDevice());
}
WriteDescriptors()
.AddBuffer(raytrace.descriptorSet0.get(), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uniforms.Buffer.get(), 0, sizeof(Uniforms))
.AddBuffer(raytrace.descriptorSet0.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetRaytrace()->GetSurfaceIndexBuffer())
.AddBuffer(raytrace.descriptorSet0.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetRaytrace()->GetSurfaceBuffer())
.AddBuffer(raytrace.descriptorSet0.get(), 1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetSurfaceIndexBuffer())
.AddBuffer(raytrace.descriptorSet0.get(), 2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetSurfaceBuffer())
.AddBuffer(raytrace.descriptorSet0.get(), 3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, lights.Buffer.get())
.AddBuffer(raytrace.descriptorSet0.get(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetRaytrace()->GetPortalBuffer())
.AddBuffer(raytrace.descriptorSet0.get(), 4, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, fb->GetLevelMesh()->GetPortalBuffer())
#ifdef USE_DRAWINDIRECT
.AddBuffer(raytrace.descriptorSet0.get(), 5, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, drawindexed.ConstantsBuffer.get(), 0, drawindexed.BufferSize * sizeof(LightmapRaytracePC))
#endif
.Execute(fb->GetDevice());
}
void VkLightmap::CreateResolvePipeline()
void VkLightmapper::CreateResolvePipeline()
{
resolve.descriptorSetLayout = DescriptorSetLayoutBuilder()
.AddBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT)
@ -890,7 +897,7 @@ void VkLightmap::CreateResolvePipeline()
.Create(fb->GetDevice());
}
void VkLightmap::CreateBlurPipeline()
void VkLightmapper::CreateBlurPipeline()
{
blur.descriptorSetLayout = DescriptorSetLayoutBuilder()
.AddBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT)
@ -946,7 +953,7 @@ void VkLightmap::CreateBlurPipeline()
.Create(fb->GetDevice());
}
void VkLightmap::CreateCopyPipeline()
void VkLightmapper::CreateCopyPipeline()
{
copy.descriptorSetLayout = DescriptorSetLayoutBuilder()
.AddBinding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT)
@ -1002,7 +1009,7 @@ void VkLightmap::CreateCopyPipeline()
.Create(fb->GetDevice());
}
void VkLightmap::CreateBakeImage()
void VkLightmapper::CreateBakeImage()
{
int width = bakeImageSize;
int height = bakeImageSize;
@ -1087,7 +1094,7 @@ void VkLightmap::CreateBakeImage()
.Execute(fb->GetDevice());
}
void VkLightmap::CreateUniformBuffer()
void VkLightmapper::CreateUniformBuffer()
{
VkDeviceSize align = fb->GetDevice()->PhysicalDevice.Properties.Properties.limits.minUniformBufferOffsetAlignment;
uniforms.StructStride = (sizeof(Uniforms) + align - 1) / align * align;
@ -1105,7 +1112,7 @@ void VkLightmap::CreateUniformBuffer()
.Create(fb->GetDevice());
}
void VkLightmap::CreateLightBuffer()
void VkLightmapper::CreateLightBuffer()
{
size_t size = sizeof(LightInfo) * lights.BufferSize;
@ -1124,7 +1131,7 @@ void VkLightmap::CreateLightBuffer()
lights.Pos = 0;
}
void VkLightmap::CreateTileBuffer()
void VkLightmapper::CreateTileBuffer()
{
size_t size = sizeof(CopyTileInfo) * copytiles.BufferSize;
@ -1142,7 +1149,7 @@ void VkLightmap::CreateTileBuffer()
copytiles.Tiles = (CopyTileInfo*)copytiles.Buffer->Map(0, size);
}
void VkLightmap::CreateDrawIndexedBuffer()
void VkLightmapper::CreateDrawIndexedBuffer()
{
size_t size1 = sizeof(VkDrawIndexedIndirectCommand) * drawindexed.BufferSize;
size_t size2 = sizeof(LightmapRaytracePC) * drawindexed.BufferSize;

View file

@ -2,8 +2,7 @@
#include "hw_levelmesh.h"
#include "zvulkan/vulkanobjects.h"
#include "dp_rect_pack/dp_rect_pack.h"
#include "framework/zstring.h"
#include <dp_rect_pack/dp_rect_pack.h>
typedef dp::rect_pack::RectPacker<int> RectPacker;
@ -96,9 +95,9 @@ struct LightInfo
float Padding3;
};
struct SelectedSurface
struct SelectedTile
{
LevelMeshSurface* Surface = nullptr;
LightmapTile* Tile = nullptr;
int X = -1;
int Y = -1;
bool Rendered = false;
@ -120,20 +119,20 @@ struct CopyTileInfo
static_assert(sizeof(CopyTileInfo) == sizeof(int32_t) * 8);
class VkLightmap
class VkLightmapper
{
public:
VkLightmap(VulkanRenderDevice* fb);
~VkLightmap();
VkLightmapper(VulkanRenderDevice* fb);
~VkLightmapper();
void BeginFrame();
void Raytrace(const TArray<LevelMeshSurface*>& surfaces);
void Raytrace(const TArray<LightmapTile*>& surfaces);
void SetLevelMesh(LevelMesh* level);
private:
void ReleaseResources();
void SelectSurfaces(const TArray<LevelMeshSurface*>& surfaces);
void SelectTiles(const TArray<LightmapTile*>& surfaces);
void UploadUniforms();
void Render();
void Resolve();
@ -156,16 +155,18 @@ private:
int GetRaytracePipelineIndex();
static FString LoadPrivateShaderLump(const char* lumpname);
static FString LoadPublicShaderLump(const char* lumpname) { return LoadPrivateShaderLump(lumpname); }
static FString LoadPublicShaderLump(const char* lumpname);
static ShaderIncludeResult OnInclude(FString headerName, FString includerName, size_t depth, bool system);
FVector3 SwapYZ(const FVector3& v) { return FVector3(v.X, v.Z, v.Y); }
VulkanRenderDevice* fb = nullptr;
LevelMesh* mesh = nullptr;
bool useRayQuery = true;
TArray<SelectedSurface> selectedSurfaces;
TArray<TArray<SelectedSurface*>> copylists;
TArray<SelectedTile> selectedTiles;
TArray<TArray<SelectedTile*>> copylists;
TArray<LevelMeshLight> templightlist;
struct

View file

@ -1,7 +1,7 @@
#include "vk_renderdevice.h"
#include "vk_raytrace.h"
#include "vk_lightmap.h"
#include "vk_levelmesh.h"
#include "vk_lightmapper.h"
#include "stacktrace.h"
#include <zvulkan/vulkanbuilders.h>
#include <zvulkan/vulkancompatibledevice.h>
@ -37,8 +37,8 @@ VulkanRenderDevice::VulkanRenderDevice()
commands = std::make_unique<VkCommandBufferManager>(this);
descriptors = std::make_unique<VkDescriptorSetManager>(this);
textures = std::make_unique<VkTextureManager>(this);
raytrace = std::make_unique<VkRaytrace>(this);
lightmap = std::make_unique<VkLightmap>(this);
levelmesh = std::make_unique<VkLevelMesh>(this);
lightmapper = std::make_unique<VkLightmapper>(this);
}
VulkanRenderDevice::~VulkanRenderDevice()

View file

@ -6,8 +6,8 @@
#include "zvulkan/vulkanbuilders.h"
#include <stdexcept>
class VkRaytrace;
class VkLightmap;
class VkLevelMesh;
class VkLightmapper;
class VkCommandBufferManager;
class VkDescriptorSetManager;
class VkTextureManager;
@ -22,8 +22,8 @@ public:
VkCommandBufferManager* GetCommands() { return commands.get(); }
VkDescriptorSetManager* GetDescriptorSetManager() { return descriptors.get(); }
VkTextureManager* GetTextureManager() { return textures.get(); }
VkRaytrace* GetRaytrace() { return raytrace.get(); }
VkLightmap* GetLightmap() { return lightmap.get(); }
VkLevelMesh* GetLevelMesh() { return levelmesh.get(); }
VkLightmapper* GetLightmapper() { return lightmapper.get(); }
int GetBindlessTextureIndex(FTextureID texture) { return -1; }
@ -34,8 +34,8 @@ private:
std::unique_ptr<VkCommandBufferManager> commands;
std::unique_ptr<VkDescriptorSetManager> descriptors;
std::unique_ptr<VkTextureManager> textures;
std::unique_ptr<VkRaytrace> raytrace;
std::unique_ptr<VkLightmap> lightmap;
std::unique_ptr<VkLevelMesh> levelmesh;
std::unique_ptr<VkLightmapper> lightmapper;
};
class VkCommandBufferManager
@ -128,8 +128,8 @@ class VkDescriptorSetManager
public:
VkDescriptorSetManager(VulkanRenderDevice* fb);
VulkanDescriptorSetLayout* GetBindlessSetLayout() { return BindlessDescriptorSetLayout.get(); }
VulkanDescriptorSet* GetBindlessDescriptorSet() { return BindlessDescriptorSet.get(); }
VulkanDescriptorSetLayout* GetBindlessLayout() { return BindlessDescriptorSetLayout.get(); }
VulkanDescriptorSet* GetBindlessSet() { return BindlessDescriptorSet.get(); }
void UpdateBindlessDescriptorSet();
int AddBindlessTextureIndex(VulkanImageView* imageview, VulkanSampler* sampler);