Just a Vulkan triangle
parent
ba8405e51f
commit
41eb636826
|
@ -9,3 +9,5 @@
|
|||
*.tga filter=lfs diff=lfs merge=lfs -text
|
||||
*.tiff filter=lfs diff=lfs merge=lfs -text
|
||||
*.BMP filter=lfs diff=lfs merge=lfs -text
|
||||
# Shaders
|
||||
*.spv filter=lfs diff=lfs merge=lfs -text
|
||||
|
|
|
@ -41,8 +41,8 @@ file(GLOB_RECURSE CORE_SOURCES "src/core/*.cpp" "include/tracy/TracyClient.cpp")
|
|||
set(CORE_HEADERS "include/toml++" "include/robin_hood" "include/libguarded" "include/tracy")
|
||||
set(CORE_LIBS pthread dl glm::glm_static enet::enet_static zstd::zstd_static)
|
||||
|
||||
file(GLOB_RECURSE CLIENT_SOURCES "src/client/*.cpp" "include/imgui/*.cpp" "include/meshoptimizer/*.cpp" "include/gl3w/gl3w.c")
|
||||
set(CLIENT_HEADERS "include/imgui" "include/meshoptimizer" "include/gl3w")
|
||||
file(GLOB_RECURSE CLIENT_SOURCES "src/client/*.cpp" "include/imgui/*.cpp" "include/meshoptimizer/*.cpp" "include/gl3w/gl3w.c" "include/volk/volk.c")
|
||||
set(CLIENT_HEADERS "include/imgui" "include/meshoptimizer" "include/gl3w" "include/volk")
|
||||
set(CLIENT_LIBS glfw)
|
||||
|
||||
file(GLOB_RECURSE SERVER_SOURCES "src/server/*.cpp" "include/FastNoiseSIMD/*.cpp")
|
||||
|
|
|
@ -54,6 +54,7 @@ To get a local copy up and running, follow these simple steps.
|
|||
|
||||
* Python: utility scripts
|
||||
* Tracy v0.7: profiling
|
||||
* glslc: compile vk shaders
|
||||
|
||||
### Installation
|
||||
|
||||
|
|
3
TODO.md
3
TODO.md
|
@ -3,7 +3,8 @@
|
|||
## Hello screen again
|
||||
|
||||
- [~] Extract OpenGL
|
||||
- [ ] Minimal Vulkan
|
||||
- Rename Passes/Programs as Pipelines
|
||||
- [~] Minimal Vulkan
|
||||
- [ ] ImGui
|
||||
- [ ] Config (yaml)
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,10 @@
|
|||
#version 450
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(fragColor, 1.0);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
#version 450
|
||||
#extension GL_ARB_separate_shader_objects : enable
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
|
||||
vec2 positions[3] = vec2[](
|
||||
vec2(0.0, -0.5),
|
||||
vec2(0.5, 0.5),
|
||||
vec2(-0.5, 0.5)
|
||||
);
|
||||
|
||||
vec3 colors[3] = vec3[](
|
||||
vec3(1.0, 0.0, 0.0),
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
vec3(0.0, 0.0, 1.0)
|
||||
);
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||
fragColor = colors[gl_VertexIndex];
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
#version 450 core
|
||||
|
||||
layout (constant_id = 0) const bool FOG = true;
|
||||
layout (constant_id = 1) const bool PBR = true;
|
||||
layout (constant_id = 2) const bool TRIPLANAR = false;
|
||||
layout (constant_id = 3) const bool STOCHASTIC = false;
|
||||
layout (constant_id = 4) const bool BLEND = true;
|
||||
|
||||
layout (constant_id = 16) UNIT_SIZE = 8;
|
||||
|
||||
// Ouput data
|
||||
layout(location = 0) out vec3 color;
|
||||
|
||||
uniform sampler2DArray TextureAtlas;
|
||||
uniform sampler2DArray NormalAtlas;
|
||||
uniform sampler2DArray HOSAtlas;
|
||||
|
||||
uniform mat4 View;
|
||||
uniform vec3 FogColor;
|
||||
|
||||
#ifdef GEOMETRY
|
||||
in GeometryData
|
||||
#else
|
||||
in VertexData
|
||||
#endif
|
||||
{
|
||||
vec3 Position_modelspace;
|
||||
#ifdef GEOMETRY
|
||||
flat uint Textures[3];
|
||||
vec3 TextureRatio;
|
||||
#else
|
||||
flat uint Texture;
|
||||
#endif
|
||||
|
||||
vec3 FaceNormal_modelspace;
|
||||
vec3 FaceNormal_worldspace;
|
||||
vec3 EyeDirection_cameraspace;
|
||||
vec3 LightDirection_cameraspace;
|
||||
|
||||
float Depth;
|
||||
} vs;
|
||||
|
||||
vec3 expand(vec3 v) {
|
||||
return (v - 0.5) * 2;
|
||||
}
|
||||
|
||||
vec2 hash2D(vec2 s) {
|
||||
// magic numbers
|
||||
return fract(sin(mod(vec2(dot(s, vec2(127.1, 311.7)), dot(s, vec2(269.5, 183.3))), 3.14159)) * 43758.5453);
|
||||
}
|
||||
|
||||
vec4 textureStochastic(sampler2DArray sample, vec3 UV) {
|
||||
if(STOCHASTIC) {
|
||||
// triangular by approx 2*sqrt(3)
|
||||
vec2 skewUV = mat2(1.0, 0.0, -0.57735027, 1.15470054) * (UV.xy * 3.46400);
|
||||
|
||||
// vertex id and barrycentric coords
|
||||
vec2 vxID = vec2(floor(skewUV));
|
||||
vec3 barry = vec3(fract(skewUV), 0);
|
||||
barry.z = 1.0 - barry.x - barry.y;
|
||||
|
||||
vec3 BW_vx0;
|
||||
vec3 BW_vx1;
|
||||
vec3 BW_vx2;
|
||||
vec3 BW_vx3;
|
||||
|
||||
if(barry.z > 0) {
|
||||
BW_vx0 = vec3(vxID, 0);
|
||||
BW_vx1 = vec3(vxID + vec2(0, 1), 0);
|
||||
BW_vx2 = vec3(vxID + vec2(1, 0), 0);
|
||||
BW_vx3 = barry.zyx;
|
||||
} else {
|
||||
BW_vx0 = vec3(vxID + vec2(1, 1), 0);
|
||||
BW_vx1 = vec3(vxID + vec2(1, 0), 0);
|
||||
BW_vx2 = vec3(vxID + vec2(0, 1), 0);
|
||||
BW_vx3 = vec3(-barry.z, 1.0 - barry.y, 1.0 - barry.x);
|
||||
}
|
||||
|
||||
vec2 dx = dFdx(UV.xy);
|
||||
vec2 dy = dFdy(UV.xy);
|
||||
|
||||
return textureGrad(sample, vec3(UV.xy + hash2D(BW_vx0.xy), UV.z), dx, dy) * BW_vx3.x +
|
||||
textureGrad(sample, vec3(UV.xy + hash2D(BW_vx1.xy), UV.z), dx, dy) * BW_vx3.y +
|
||||
textureGrad(sample, vec3(UV.xy + hash2D(BW_vx2.xy), UV.z), dx, dy) * BW_vx3.z;
|
||||
} else {
|
||||
return texture(sample, UV);
|
||||
}
|
||||
}
|
||||
|
||||
vec4 getTexture(sampler2DArray sample, vec2 UV) {
|
||||
#ifdef GEOMETRY
|
||||
if(BLEND) {
|
||||
vec4 colx = textureStochastic(sample, vec3(UV, vs.Textures[0]));
|
||||
if(vs.Textures[1] == vs.Textures[0]) {
|
||||
return vs.Textures[2] == vs.Textures[0] ? colx :
|
||||
mix(colx, textureStochastic(sample, vec3(UV, vs.Textures[2])), vs.TextureRatio.z);
|
||||
} else {
|
||||
vec4 coly = textureStochastic(sample, vec3(UV, vs.Textures[1]));
|
||||
return vs.Textures[2] == vs.Textures[0] ? mix(colx, coly, vs.TextureRatio.y) : (
|
||||
vs.Textures[2] == vs.Textures[1] ? mix(coly, colx, vs.TextureRatio.x) :
|
||||
colx * vs.TextureRatio.x + coly * vs.TextureRatio.y + textureStochastic(sample, vec3(UV, vs.Textures[2])) * vs.TextureRatio.z);
|
||||
}
|
||||
} else {
|
||||
int mainTexture = vs.TextureRatio.x >= vs.TextureRatio.y ?
|
||||
(vs.TextureRatio.x >= vs.TextureRatio.z ? 0 : 2) :
|
||||
(vs.TextureRatio.y >= vs.TextureRatio.z ? 1 : 2);
|
||||
return textureStochastic(sample, vec3(UV, vs.Textures[mainTexture]));
|
||||
}
|
||||
#else
|
||||
return textureStochastic(sample, vec3(UV, vs.Texture));
|
||||
#endif
|
||||
}
|
||||
|
||||
vec3 getTriTexture(sampler2DArray sample, vec2 crdx, vec2 crdy, vec2 crdz, vec3 weights) {
|
||||
return getTexture(sample, crdx).rgb * weights.x +
|
||||
getTexture(sample, crdy).rgb * weights.y +
|
||||
getTexture(sample, crdz).rgb * weights.z;
|
||||
}
|
||||
|
||||
void main() {
|
||||
float texScale = 1. / UNIT_SIZE;
|
||||
if(TRIPLANAR) {
|
||||
// Triplanar
|
||||
float plateauSize = 0.001;
|
||||
float transitionSpeed = 2;
|
||||
|
||||
vec3 blendWeights = abs(vs.FaceNormal_modelspace);
|
||||
blendWeights = blendWeights - plateauSize;
|
||||
blendWeights = pow(max(blendWeights, 0), vec3(transitionSpeed));
|
||||
vec2 UVx = vs.Position_modelspace.yz * texScale;
|
||||
vec2 UVy = vs.Position_modelspace.zx * texScale;
|
||||
vec2 UVz = vs.Position_modelspace.xy * texScale;
|
||||
|
||||
vec3 tex = getTriTexture(TextureAtlas, UVx, UVy, UVz, blendWeights);
|
||||
|
||||
if(PBR) {
|
||||
// Whiteout normal blend
|
||||
vec3 texNx = expand(getTexture(NormalAtlas, UVx).rgb);
|
||||
vec3 texNy = expand(getTexture(NormalAtlas, UVy).rgb);
|
||||
vec3 texNz = expand(getTexture(NormalAtlas, UVz).rgb);
|
||||
// Swizzle world normals into tangent space and apply Whiteout blend
|
||||
texNx = vec3(texNx.xy + vs.FaceNormal_worldspace.zy, abs(texNx.z) * vs.FaceNormal_worldspace.x);
|
||||
texNy = vec3(texNy.xy + vs.FaceNormal_worldspace.xz, abs(texNy.z) * vs.FaceNormal_worldspace.y);
|
||||
texNz = vec3(texNz.xy + vs.FaceNormal_worldspace.xy, abs(texNz.z) * vs.FaceNormal_worldspace.z);
|
||||
// Swizzle tangent normals to match world orientation and triblend
|
||||
vec3 worldNormal = normalize(texNx.zyx * blendWeights.x + texNy.xzy * blendWeights.y +texNz.xyz * blendWeights.z);
|
||||
|
||||
vec3 texHOS = getTriTexture(HOSAtlas, UVx, UVy, UVz, blendWeights);
|
||||
}
|
||||
} else {
|
||||
// Cheap planar
|
||||
vec3 blendWeights = abs(vs.FaceNormal_modelspace);
|
||||
vec3 nrm = normalize(pow(blendWeights, vec3(80 / sqrt(UNIT_SIZE))));
|
||||
vec2 UV = (vec2(vs.Position_modelspace.xy * nrm.z) + vec2(vs.Position_modelspace.yz * nrm.x) + vec2(vs.Position_modelspace.zx * nrm.y)) * texScale;
|
||||
|
||||
vec3 tex = getTexture(TextureAtlas, UV).rgb;
|
||||
if(PBR) {
|
||||
vec3 texN = expand(getTexture(NormalAtlas, UV).rgb);
|
||||
// Swizzle world normals into tangent space and apply Whiteout blend
|
||||
// Swizzle tangent normals to match world orientation and triblend
|
||||
vec3 worldNormal = normalize(vec3(texN.xy + vs.FaceNormal_worldspace.zy, abs(texN.z) * vs.FaceNormal_worldspace.x).zyx * blendWeights.x +
|
||||
vec3(texN.xy + vs.FaceNormal_worldspace.xz, abs(texN.z) * vs.FaceNormal_worldspace.y).xzy * blendWeights.y +
|
||||
vec3(texN.xy + vs.FaceNormal_worldspace.xy, abs(texN.z) * vs.FaceNormal_worldspace.z).xyz * blendWeights.z);
|
||||
|
||||
vec3 texHOS = getTexture(HOSAtlas, UV).rgb;
|
||||
}
|
||||
}
|
||||
|
||||
// Colors
|
||||
if(PBR) {
|
||||
// Texture properties
|
||||
vec3 TextureDiffuseColor = tex;
|
||||
vec3 TextureAmbientColor = vec3(.1) * TextureDiffuseColor * texHOS.y;
|
||||
vec3 TextureSpecularColor = vec3(.8) * texHOS.z;
|
||||
|
||||
vec3 Normal_cameraspace = normalize((View * vec4(worldNormal,0)).xyz);
|
||||
|
||||
// Light emission properties
|
||||
// You probably want to put them as uniforms
|
||||
vec3 LightColor = vec3(1, 0.9, 0.9);
|
||||
float LightPower = 1.2f;
|
||||
|
||||
// Distance to the light
|
||||
float distance = 1.0f;//length( LightPosition_worldspace - Position_worldspace );
|
||||
|
||||
// Direction of the light (from the fragment to the light)
|
||||
vec3 l = normalize(vs.LightDirection_cameraspace);
|
||||
// Cosine of the angle between the normal and the light direction,
|
||||
// clamped above 0
|
||||
// - light is at the vertical of the triangle -> 1
|
||||
// - light is perpendiular to the triangle -> 0
|
||||
// - light is behind the triangle -> 0
|
||||
float cosTheta = clamp(dot(Normal_cameraspace,l), 0,1 );
|
||||
|
||||
// Eye vector (towards the camera)
|
||||
vec3 E = normalize(vs.EyeDirection_cameraspace);
|
||||
// Direction in which the triangle reflects the light
|
||||
vec3 R = reflect(-l,Normal_cameraspace);
|
||||
// Cosine of the angle between the Eye vector and the Reflect vector,
|
||||
// clamped to 0
|
||||
// - Looking into the reflection -> 1
|
||||
// - Looking elsewhere -> < 1
|
||||
float cosAlpha = clamp( dot( E,R ), 0,1 );
|
||||
|
||||
float visibility=1.0;
|
||||
|
||||
// MAYBE: shadow
|
||||
|
||||
color =
|
||||
// Ambient : simulates indirect lighting
|
||||
TextureAmbientColor +
|
||||
// Diffuse : "color" of the object
|
||||
visibility * TextureDiffuseColor * LightColor * LightPower * cosTheta / (distance * distance) +
|
||||
// Specular : reflective highlight, like a mirror
|
||||
visibility * TextureSpecularColor * LightColor * LightPower * pow(cosAlpha,5) / (distance * distance);
|
||||
} else {
|
||||
color = tex;
|
||||
}
|
||||
if(FOG) {
|
||||
float ratio = exp(vs.Depth * 0.69)-1;
|
||||
color = mix(color, pow(FogColor, vec3(2.2)), clamp(ratio, 0, 1));
|
||||
}
|
||||
color = pow(color, vec3(1.0 / 2.2));
|
||||
if(color.r > 1 || color.g > 1 || color.b > 1) {
|
||||
color = vec3(1, 0, 0); //TODO: bloom
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
#version 450 core
|
||||
|
||||
layout (constant_id = 0) const bool FOG = true;
|
||||
layout (constant_id = 1) const bool PBR = true;
|
||||
|
||||
layout (triangles) in;
|
||||
layout (triangle_strip, max_vertices = 3) out;
|
||||
|
||||
in VertexData {
|
||||
vec3 Position_modelspace;
|
||||
flat uint Texture;
|
||||
vec3 FaceNormal_modelspace;
|
||||
|
||||
vec3 FaceNormal_worldspace;
|
||||
vec3 EyeDirection_cameraspace;
|
||||
vec3 LightDirection_cameraspace;
|
||||
|
||||
float Depth;
|
||||
} vs_in[];
|
||||
|
||||
out GeometryData {
|
||||
vec3 Position_modelspace;
|
||||
flat uint Textures[3];
|
||||
vec3 TextureRatio;
|
||||
vec3 FaceNormal_modelspace;
|
||||
|
||||
vec3 FaceNormal_worldspace;
|
||||
vec3 EyeDirection_cameraspace;
|
||||
vec3 LightDirection_cameraspace;
|
||||
|
||||
float Depth;
|
||||
} gs;
|
||||
|
||||
void main() {
|
||||
for(int i = 0; i < gl_in.length(); i++) {
|
||||
|
||||
gl_Position = gl_in[i].gl_Position;
|
||||
gs.Position_modelspace = vs_in[i].Position_modelspace;
|
||||
gs.FaceNormal_modelspace = vs_in[i].FaceNormal_modelspace;
|
||||
|
||||
gs.Textures[i] = vs_in[i].Texture;
|
||||
switch(int(mod(i,3))) {
|
||||
case 0:
|
||||
gs.TextureRatio = vec3(1,0,0);
|
||||
break;
|
||||
case 1:
|
||||
gs.TextureRatio = vec3(0,1,0);
|
||||
break;
|
||||
case 2:
|
||||
gs.TextureRatio = vec3(0,0,1);
|
||||
break;
|
||||
default:
|
||||
gs.TextureRatio = vec3(0);
|
||||
break;
|
||||
};
|
||||
|
||||
if(PBR) {
|
||||
gs.FaceNormal_worldspace = vs_in[i].FaceNormal_worldspace;
|
||||
gs.EyeDirection_cameraspace = vs_in[i].EyeDirection_cameraspace;
|
||||
gs.LightDirection_cameraspace = vs_in[i].LightDirection_cameraspace;
|
||||
}
|
||||
#ifdef SHADOW
|
||||
gs.ShadowCoord = vs_in[i].ShadowCoord;
|
||||
#endif
|
||||
if(FOG) {
|
||||
gs.Depth = vs_in[i].Depth;
|
||||
}
|
||||
EmitVertex();
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
#version 450 core
|
||||
|
||||
layout (constant_id = 0) const bool FOG = true;
|
||||
layout (constant_id = 1) const bool PBR = true;
|
||||
// FS spe
|
||||
layout (constant_id = 5) const bool DO_CURVATURE = false;
|
||||
layout (constant_id = 6) const bool CURV_DEPTH = true;
|
||||
|
||||
layout(location = 0) in vec3 Position_modelspace;
|
||||
layout(location = 1) in uint Texture_model;
|
||||
layout(location = 2) in vec3 Normal_modelspace;
|
||||
|
||||
out VertexData {
|
||||
vec3 Position_modelspace;
|
||||
flat uint Texture;
|
||||
vec3 FaceNormal_modelspace;
|
||||
|
||||
vec3 FaceNormal_worldspace;
|
||||
vec3 EyeDirection_cameraspace;
|
||||
vec3 LightDirection_cameraspace;
|
||||
|
||||
float Depth;
|
||||
} vs;
|
||||
|
||||
#ifdef INSTANCED
|
||||
layout(location = 6) in mat4 Model;
|
||||
#else
|
||||
uniform mat4 Model;
|
||||
#endif
|
||||
uniform mat4 View;
|
||||
uniform mat4 Proj;
|
||||
|
||||
uniform vec4 SphereProj;
|
||||
uniform float Curvature;
|
||||
|
||||
uniform vec3 LightInvDirection_worldspace;
|
||||
uniform float FogDepth;
|
||||
|
||||
void main(){
|
||||
vs.Position_modelspace = Position_modelspace;
|
||||
|
||||
if(DO_CURVATURE) {
|
||||
if(Curvature > 0) {
|
||||
vec3 Position_areaspace = Position_modelspace + SphereProj.xyz;
|
||||
vec2 sph = vec2(acos(Position_areaspace.z / length(Position_areaspace.xyz)), atan(Position_areaspace.y, Position_areaspace.x));
|
||||
if(CURV_DEPTH) {
|
||||
float radius = max(max(abs(Position_areaspace.x), abs(Position_areaspace.y)), abs(Position_areaspace.z));
|
||||
} else {
|
||||
float radius = SphereProj.w;
|
||||
}
|
||||
vs.Position_modelspace = mix(vs.Position_modelspace, vec3(sin(sph.x)*cos(sph.y), sin(sph.x)*sin(sph.y), cos(sph.x)) * radius - SphereProj.xyz, Curvature);
|
||||
}
|
||||
}
|
||||
|
||||
vec4 Position_cameraspace = View * Model * vec4(vs.Position_modelspace, 1);
|
||||
gl_Position = Proj * Position_cameraspace;
|
||||
|
||||
if(FOG) {
|
||||
vs.Depth = length(Position_cameraspace.xyz) / FogDepth;
|
||||
}
|
||||
|
||||
vs.Texture = Texture_model;
|
||||
|
||||
vs.FaceNormal_modelspace = normalize(Normal_modelspace);
|
||||
if(PBR)
|
||||
// TODO: correct normal curvature
|
||||
vs.FaceNormal_worldspace = normalize((Model * vec4(vs.FaceNormal_modelspace, 0)).xyz);
|
||||
|
||||
// Vector that goes from the vertex to the camera, in camera space.
|
||||
// In camera space, the camera is at the origin (0,0,0).
|
||||
vs.EyeDirection_cameraspace = vec3(0,0,0) - Position_cameraspace.xyz;
|
||||
|
||||
// Vector that goes from the vertex to the light, in camera space
|
||||
vs.LightDirection_cameraspace = (View * vec4(LightInvDirection_worldspace,0)).xyz;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
BASEDIR=$(dirname "$0")
|
||||
TARGETDIR="$BASEDIR/../content/shaders"
|
||||
|
||||
# Tris
|
||||
glslc -fshader-stage=vertex $BASEDIR/Tris.vs -o $TARGETDIR/Tris.vs.spv
|
||||
glslc -fshader-stage=fragment $BASEDIR/Tris.fs -o $TARGETDIR/Tris.fs.spv
|
||||
|
||||
# Color
|
||||
glslc -fshader-stage=vertex $TARGETDIR/Color.vs -o $TARGETDIR/Color.vs.spv
|
||||
glslc -fshader-stage=fragment $TARGETDIR/Color.fs -o $TARGETDIR/Color.fs.spv
|
||||
|
||||
# Sky
|
||||
glslc -fshader-stage=vertex $TARGETDIR/Sky.vs -o $TARGETDIR/Sky.vs.spv
|
||||
glslc -fshader-stage=fragment $TARGETDIR/Sky.fs -o $TARGETDIR/Sky.fs.spv
|
||||
|
||||
# Voxel
|
||||
glslc -fshader-stage=vertex $BASEDIR/Voxel.vs -o $TARGETDIR/Voxel.vs.spv
|
||||
glslc -fshader-stage=fragment $BASEDIR/Voxel.fs -o $TARGETDIR/Voxel.fs.spv
|
||||
glslc -fshader-stage=vertex $BASEDIR/Voxel.vs -DINSTANCED -o $TARGETDIR/Voxel.vs.ins.spv
|
||||
glslc -fshader-stage=fragment $BASEDIR/Voxel.fs -DINSTANCED -o $TARGETDIR/Voxel.fs.ins.spv
|
||||
|
||||
glslc -fshader-stage=vertex $BASEDIR/Voxel.vs -DGEOMETRY -o $TARGETDIR/Voxel.vs.geo.spv
|
||||
glslc -fshader-stage=geometry $BASEDIR/Voxel.gs -DGEOMETRY -o $TARGETDIR/Voxel.gs.geo.spv
|
||||
glslc -fshader-stage=fragment $BASEDIR/Voxel.fs -DGEOMETRY -o $TARGETDIR/Voxel.fs.geo.spv
|
||||
glslc -fshader-stage=vertex $BASEDIR/Voxel.vs -DGEOMETRY -DINSTANCED -o $TARGETDIR/Voxel.vs.geo.ins.spv
|
||||
glslc -fshader-stage=geometry $BASEDIR/Voxel.gs -DGEOMETRY -DINSTANCED -o $TARGETDIR/Voxel.gs.geo.ins.spv
|
||||
glslc -fshader-stage=fragment $BASEDIR/Voxel.fs -DGEOMETRY -DINSTANCED -o $TARGETDIR/Voxel.fs.geo.ins.spv
|
|
@ -1,8 +1,8 @@
|
|||
#include "Client.hpp"
|
||||
|
||||
#include "render/gl/Renderer.hpp"
|
||||
#include "render/gl/UI.hpp"
|
||||
#include "render/gl/Pipeline.hpp"
|
||||
#include "render/index.hpp"
|
||||
#include "render/UI.hpp"
|
||||
|
||||
#include "InputMap.hpp"
|
||||
#include "world/index.hpp"
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
@ -13,7 +13,7 @@ Client::Client(config::client::options& options): options(options) { }
|
|||
Client::~Client() { }
|
||||
|
||||
void Client::run(server_handle* const localHandle) {
|
||||
if (!render::gl::Renderer::Load(window, {options.renderer.clear_color}))
|
||||
if (!render::Load(window, options.preferVulkan, options.renderer))
|
||||
return;
|
||||
|
||||
window.setTargetFPS(options.window.targetFPS);
|
||||
|
@ -23,7 +23,7 @@ void Client::run(server_handle* const localHandle) {
|
|||
Controllable player(window.getPtr(), inputs, options.control);
|
||||
Camera camera(&player, options.camera);
|
||||
|
||||
auto *pipeline = static_cast<render::gl::Pipeline*>(render::Renderer::Get()->createPipeline(options.renderer));
|
||||
auto pipeline = render::Renderer::Get();
|
||||
pipeline->LightInvDir = glm::normalize(glm::vec3(-.5f, 2, -2));
|
||||
render::Renderer::Get()->loadUI(window);
|
||||
|
||||
|
@ -119,7 +119,6 @@ void Client::run(server_handle* const localHandle) {
|
|||
world->setContouring(contouring::load(options.contouring.idx, options.contouring.data));
|
||||
state.contouring = world->getContouring();
|
||||
}
|
||||
pipeline->SkyEnable = options.renderer.skybox;
|
||||
}
|
||||
{ // Rendering
|
||||
ZoneScopedN("Render");
|
||||
|
@ -135,8 +134,7 @@ void Client::run(server_handle* const localHandle) {
|
|||
{ // Chunks
|
||||
const auto pass = pipeline->beginWorldPass();
|
||||
const auto draw = [&](glm::mat4 model, buffer::Abstract *const buffer, const contouring::Abstract::area_info &area, const voxel_pos &pos) {
|
||||
pipeline->SphereProj = glm::vec4(pos, std::get<1>(area));
|
||||
pipeline->Curvature = std::get<2>(area);
|
||||
pipeline->setCurvature(glm::vec4(pos, std::get<1>(area)), std::get<2>(area));
|
||||
reports.models_count++;
|
||||
reports.tris_count += buffer->draw(pass(model));
|
||||
};
|
||||
|
@ -160,10 +158,10 @@ void Client::run(server_handle* const localHandle) {
|
|||
}
|
||||
{ // Entities
|
||||
const auto pass = pipeline->beginEntityPass();
|
||||
const auto draw = [&](const std::vector<glm::mat4>& models, buffer::Abstract *const buffer) {
|
||||
/*const auto draw = [&](const std::vector<glm::mat4>& models, buffer::Abstract *const buffer) {
|
||||
reports.models_count += models.size();
|
||||
reports.tris_count += buffer->draw(pass(models));
|
||||
};
|
||||
};*/
|
||||
//world->getEntitiesModels(draw, frustum, offset, options.voxel_density);
|
||||
}
|
||||
if(state.look_at.has_value()) { // Indicator
|
||||
|
@ -178,7 +176,7 @@ void Client::run(server_handle* const localHandle) {
|
|||
|
||||
{ // Swap buffers
|
||||
ZoneScopedN("Swap");
|
||||
pipeline->swapBuffer(window.getPtr());
|
||||
pipeline->swapBuffer(window);
|
||||
inputs.poll();
|
||||
FrameMarkNamed("Client");
|
||||
}
|
||||
|
@ -187,7 +185,6 @@ void Client::run(server_handle* const localHandle) {
|
|||
} while (!(inputs.isDown(Input::Quit) || window.shouldClose()));
|
||||
|
||||
render::UI::Unload();
|
||||
delete pipeline;
|
||||
render::Renderer::Unload();
|
||||
window.destroy();
|
||||
|
||||
|
|
|
@ -42,6 +42,11 @@ bool Window::create(const CreateInfo &opt) {
|
|||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
break;
|
||||
|
||||
case CreateInfo::Client::Type::VK:
|
||||
LOG_W("WIP client type");
|
||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||
break;
|
||||
|
||||
default:
|
||||
FATAL("Unsupported client type");
|
||||
break;
|
||||
|
@ -105,6 +110,7 @@ void Window::waitTargetFPS() {
|
|||
}
|
||||
void Window::destroy() {
|
||||
glfwDestroyWindow(ptr);
|
||||
ptr = nullptr;
|
||||
}
|
||||
|
||||
bool Window::shouldClose() { return glfwWindowShouldClose(ptr); }
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "../core/flags.hpp"
|
||||
#include <tuple>
|
||||
|
||||
typedef struct GLFWwindow GLFWwindow;
|
||||
typedef void (*GLFWframebuffersizefun)(GLFWwindow*, int, int);
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <limits.h>
|
||||
#include <iomanip>
|
||||
#include "world/Universe.hpp"
|
||||
#include "render/Pipeline.hpp"
|
||||
#include "render/Renderer.hpp"
|
||||
#include "contouring/index.hpp"
|
||||
#include "control/Camera.hpp"
|
||||
|
||||
|
@ -31,6 +31,7 @@ public:
|
|||
window.targetFPS = config["window"]["target_fps"].value_or(window.targetFPS);
|
||||
window.samples = config["window"]["samples"].value_or(window.samples);
|
||||
window.fullscreen = config["window"]["fullscreen"].value_or(window.fullscreen);
|
||||
renderer.inFlightFrames = config["window"]["parallel_frames"].value_or(renderer.inFlightFrames);
|
||||
|
||||
preferVulkan = config["render"]["prefer_vulkan"].value_or(preferVulkan);
|
||||
renderer.textures = config["render"]["textures"].value_or(renderer.textures);
|
||||
|
@ -99,7 +100,8 @@ public:
|
|||
config.insert_or_assign("window", toml::table({
|
||||
{"target_fps", window.targetFPS},
|
||||
{"samples", window.samples},
|
||||
{"fullscreen", window.fullscreen}
|
||||
{"fullscreen", window.fullscreen},
|
||||
{"parallel_frames", renderer.inFlightFrames}
|
||||
}));
|
||||
config.insert_or_assign("render", toml::table({
|
||||
{"prefer_vulkan", preferVulkan},
|
||||
|
@ -194,7 +196,7 @@ public:
|
|||
} window;
|
||||
|
||||
bool preferVulkan = true;
|
||||
render::Pipeline::options renderer;
|
||||
render::renderOptions renderer;
|
||||
int culling = 0;
|
||||
|
||||
struct {
|
||||
|
@ -219,6 +221,6 @@ public:
|
|||
int corner = 3;
|
||||
} overlay;
|
||||
|
||||
std::optional<world::client::Universe::connection> connection;
|
||||
std::optional<world::client::Universe::connection> connection = {};
|
||||
};
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "gl/pass/VoxelProgram.hpp"
|
||||
|
||||
struct GLFWwindow;
|
||||
class Camera;
|
||||
|
||||
namespace render {
|
||||
|
||||
/// Handle rendering passes and params
|
||||
class Pipeline {
|
||||
public:
|
||||
/// Rendering options
|
||||
struct options {
|
||||
/// Voxel passes
|
||||
pass::VoxelProgram::options voxel;
|
||||
/// Display skybox
|
||||
bool skybox = true;
|
||||
/// Display only wires
|
||||
bool wireframe = false;
|
||||
/// Texture pack name
|
||||
std::string textures = "1024-realistic";
|
||||
/// Textures quality
|
||||
float mipMapLOD = -.5;
|
||||
/// Textures anisotropic mapping
|
||||
int anisotropy = 0;
|
||||
/// Depth color
|
||||
glm::vec4 clear_color;
|
||||
};
|
||||
|
||||
virtual ~Pipeline() { }
|
||||
|
||||
/// Start new frame and setup
|
||||
virtual void beginFrame() = 0;
|
||||
/// Apply postprocessing
|
||||
virtual void endPass() = 0;
|
||||
/// Swap displayed image
|
||||
virtual void swapBuffer(GLFWwindow*) = 0;
|
||||
|
||||
/// Apply camera matrices
|
||||
virtual void lookFrom(const Camera&) = 0;
|
||||
|
||||
virtual void setClearColor(glm::vec4) = 0;
|
||||
};
|
||||
}
|
|
@ -1,17 +1,85 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Window.hpp"
|
||||
#include "Pipeline.hpp"
|
||||
#include "../../core/flags.hpp"
|
||||
#include "buffer/Abstract.hpp"
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <glm/glm.hpp>
|
||||
#include <functional>
|
||||
|
||||
class Window;
|
||||
class Camera;
|
||||
namespace render {
|
||||
|
||||
/// Pass options
|
||||
struct passOptions {
|
||||
/// Apply light properties
|
||||
bool pbr = true;
|
||||
/// Triplanar texture mapping
|
||||
bool triplanar = false;
|
||||
/// Transform texture UV
|
||||
bool stochastic = false;
|
||||
/// Active geometry pass
|
||||
bool geometry = false;
|
||||
/// Blend voxel with mixed materials (requires geometry)
|
||||
bool blend = true;
|
||||
/// Depth fog
|
||||
bool fog = true;
|
||||
/// Map planets to sphere
|
||||
bool curvature = true;
|
||||
/// Keep depth in sphere
|
||||
bool curv_depth = true;
|
||||
};
|
||||
|
||||
/// Rendering options
|
||||
struct renderOptions {
|
||||
/// Voxel passes
|
||||
passOptions voxel;
|
||||
/// Display skybox
|
||||
bool skybox = true;
|
||||
/// Display only wires
|
||||
bool wireframe = false;
|
||||
/// Texture pack name
|
||||
std::string textures = "1024-realistic";
|
||||
/// Textures quality
|
||||
float mipMapLOD = -.5;
|
||||
/// Textures anisotropic mapping
|
||||
int anisotropy = 0;
|
||||
/// Depth color
|
||||
glm::vec4 clear_color;
|
||||
/// Parallel processing frames
|
||||
/// Incease FPS but also a bit latency (Vulkan only)
|
||||
int inFlightFrames = 2;
|
||||
};
|
||||
|
||||
/// Rendering plateform interface
|
||||
class Renderer {
|
||||
public:
|
||||
virtual ~Renderer() { }
|
||||
|
||||
virtual Pipeline* createPipeline(const Pipeline::options&) = 0;
|
||||
glm::vec3 LightInvDir = glm::vec3(0.5f, 2, 2);
|
||||
|
||||
/// Start new frame and setup
|
||||
virtual void beginFrame() = 0;
|
||||
/// Get started world program
|
||||
virtual std::function<buffer::params(glm::mat4)> beginWorldPass() = 0;
|
||||
/// Get started entity program
|
||||
virtual std::function<buffer::params(const std::vector<glm::mat4> &)> beginEntityPass() = 0;
|
||||
/// Draw cube indicator
|
||||
virtual size_t drawIndicatorCube(glm::mat4 model) = 0;
|
||||
/// Apply postprocessing
|
||||
virtual void endPass() = 0;
|
||||
/// Swap displayed image
|
||||
virtual void swapBuffer(Window &) = 0;
|
||||
|
||||
/// Apply camera matrices
|
||||
virtual void lookFrom(const Camera &) = 0;
|
||||
|
||||
virtual void setClearColor(glm::vec4) = 0;
|
||||
virtual void setCurvature(glm::vec4, float) = 0;
|
||||
virtual void reloadShaders(const passOptions &) = 0;
|
||||
virtual void reloadTextures(const std::string &, float mipMapLOD, float anisotropy) = 0;
|
||||
|
||||
virtual void loadUI(Window&) = 0;
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/gl3w.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace buffer {
|
||||
/// Draw options
|
||||
struct params {
|
||||
/// Bind only vertices positions
|
||||
bool vertexOnly;
|
||||
/// Draw instanced model
|
||||
size_t instances = 1;
|
||||
};
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
#include "Pipeline.hpp"
|
||||
|
||||
#include "../../../core/world/materials.hpp"
|
||||
#include "../../control/Camera.hpp"
|
||||
#include <TracyOpenGL.hpp>
|
||||
|
||||
using namespace render::gl;
|
||||
|
||||
Pipeline::Pipeline(const Pipeline::options& options):
|
||||
IndicatorCubeBuffer(GL_LINES, 24, {
|
||||
glm::vec3(0, 0, 0), glm::vec3(0, 0, 1),
|
||||
glm::vec3(0, 0, 1), glm::vec3(0, 1, 1),
|
||||
glm::vec3(0, 1, 1), glm::vec3(0, 1, 0),
|
||||
glm::vec3(0, 1, 0), glm::vec3(0, 0, 0),
|
||||
glm::vec3(1, 0, 0), glm::vec3(1, 0, 1),
|
||||
glm::vec3(1, 0, 1), glm::vec3(1, 1, 1),
|
||||
glm::vec3(1, 1, 1), glm::vec3(1, 1, 0),
|
||||
glm::vec3(1, 1, 0), glm::vec3(1, 0, 0),
|
||||
glm::vec3(0, 0, 0), glm::vec3(1, 0, 0),
|
||||
glm::vec3(0, 0, 1), glm::vec3(1, 0, 1),
|
||||
glm::vec3(0, 1, 1), glm::vec3(1, 1, 1),
|
||||
glm::vec3(0, 1, 0), glm::vec3(1, 1, 0),
|
||||
}, {
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
}) {
|
||||
glGenVertexArrays(1, &VertexArrayID);
|
||||
glBindVertexArray(VertexArrayID);
|
||||
|
||||
reloadShaders(options.voxel);
|
||||
SkyPass = std::make_unique<pass::SkyProgram>();
|
||||
SkyEnable = options.skybox;
|
||||
IndicatorPass = std::make_unique<pass::ColorProgram>();
|
||||
|
||||
FogColor = glm::vec3(options.clear_color.x, options.clear_color.y, options.clear_color.z);
|
||||
loadTextures(options.textures, options.mipMapLOD, options.anisotropy);
|
||||
}
|
||||
|
||||
Pipeline::~Pipeline() {
|
||||
unloadTextures();
|
||||
glDeleteVertexArrays(1, &VertexArrayID);
|
||||
}
|
||||
|
||||
void Pipeline::beginFrame() {
|
||||
TracyGpuZone("Render");
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
std::function<buffer::params(glm::mat4)> Pipeline::beginWorldPass() {
|
||||
WorldPass->useIt();
|
||||
WorldPass->start(this);
|
||||
return [&](glm::mat4 model) {
|
||||
return WorldPass->setup(this, model);
|
||||
};
|
||||
}
|
||||
|
||||
std::function<buffer::params(const std::vector<glm::mat4> &)> Pipeline::beginEntityPass() {
|
||||
EntityPass->useIt();
|
||||
EntityPass->start(this);
|
||||
return [&](const std::vector<glm::mat4>& models) {
|
||||
return EntityPass->setup(this, models);
|
||||
};
|
||||
}
|
||||
|
||||
size_t Pipeline::drawIndicatorCube(glm::mat4 model) {
|
||||
IndicatorPass->useIt();
|
||||
return IndicatorCubeBuffer.draw(IndicatorPass->setup(this, model));
|
||||
}
|
||||
|
||||
void Pipeline::endPass() {
|
||||
if(SkyEnable) {
|
||||
SkyPass->draw(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Pipeline::swapBuffer(GLFWwindow* ptr) {
|
||||
TracyGpuZone("Swap");
|
||||
glfwSwapBuffers(ptr);
|
||||
TracyGpuCollect;
|
||||
}
|
||||
|
||||
void Pipeline::reloadShaders(const pass::VoxelProgram::options& options) {
|
||||
WorldPass = std::make_unique<pass::WorldProgram>(options);
|
||||
EntityPass = std::make_unique<pass::EntityProgram>(options);
|
||||
}
|
||||
void Pipeline::reloadTextures(const std::string& texturePath, float mipMapLOD, float anisotropy) {
|
||||
unloadTextures();
|
||||
loadTextures(texturePath, mipMapLOD, anisotropy);
|
||||
}
|
||||
|
||||
void Pipeline::unloadTextures() {
|
||||
glDeleteTextures(1, &HOSAtlas);
|
||||
glDeleteTextures(1, &NormalAtlas);
|
||||
glDeleteTextures(1, &TextureAtlas);
|
||||
}
|
||||
void Pipeline::loadTextures(const std::string& texturePath, float mipMapLOD, float anisotropy) {
|
||||
std::vector<std::string> terrainTextures;
|
||||
for(const auto& texture: world::materials::textures) {
|
||||
terrainTextures.emplace_back(texturePath + "/terrain/" + texture);
|
||||
}
|
||||
const auto ani = anisotropy >= 1 ? (1 << (static_cast<int>(anisotropy)-1)) : 0;
|
||||
TextureAtlas = pass::Program::loadTextureArray(terrainTextures, "", mipMapLOD, ani);
|
||||
NormalAtlas = pass::Program::loadTextureArray(terrainTextures, ".nrm", mipMapLOD, ani);
|
||||
HOSAtlas = pass::Program::loadTextureArray(terrainTextures, ".hos", mipMapLOD, ani);
|
||||
|
||||
Skybox = pass::Program::loadTextureCube(texturePath + "/sky/Space_tray");
|
||||
}
|
||||
|
||||
void Pipeline::lookFrom(const Camera& camera) {
|
||||
ProjectionMatrix = camera.getProjectionMatrix();
|
||||
ViewMatrix = camera.getViewMatrix();
|
||||
FogDepth = camera.getDepth();
|
||||
}
|
||||
|
||||
void Pipeline::setClearColor(glm::vec4 c) {
|
||||
FogColor = c;
|
||||
glClearColor(c.r, c.g, c.b, c.a);
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "../Pipeline.hpp"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <GL/gl3w.h>
|
||||
#include "pass/WorldProgram.hpp"
|
||||
#include "pass/EntityProgram.hpp"
|
||||
#include "pass/SkyProgram.hpp"
|
||||
#include "pass/ColorProgram.hpp"
|
||||
#include "buffer/Colored.hpp"
|
||||
|
||||
namespace render::gl {
|
||||
|
||||
/// Handle OpenGL rendering passes and params
|
||||
class Pipeline final: public render::Pipeline {
|
||||
public:
|
||||
Pipeline(const options&);
|
||||
Pipeline(Pipeline &&) = delete;
|
||||
Pipeline(const Pipeline &) = delete;
|
||||
Pipeline &operator=(Pipeline &&) = delete;
|
||||
Pipeline &operator=(const Pipeline &) = delete;
|
||||
~Pipeline();
|
||||
|
||||
glm::vec3 LightInvDir = glm::vec3(0.5f, 2, 2);
|
||||
glm::vec3 FogColor;
|
||||
GLfloat FogDepth;
|
||||
|
||||
/// Sphere bending
|
||||
/// offset.xyz radius.w
|
||||
glm::vec4 SphereProj;
|
||||
/// Ratio between spherical and cartesian
|
||||
float Curvature;
|
||||
|
||||
bool SkyEnable;
|
||||
|
||||
glm::mat4 getProjectionMatrix() const {
|
||||
return ProjectionMatrix;
|
||||
}
|
||||
glm::mat4 getViewMatrix() const {
|
||||
return ViewMatrix;
|
||||
}
|
||||
|
||||
GLuint getTextureAtlas() const {
|
||||
return TextureAtlas;
|
||||
}
|
||||
GLuint getNormalAtlas() const {
|
||||
return NormalAtlas;
|
||||
}
|
||||
GLuint getHOSAtlas() const {
|
||||
return HOSAtlas;
|
||||
}
|
||||
GLuint getSkyTexture() const {
|
||||
return Skybox;
|
||||
}
|
||||
|
||||
/// Start new frame and setup
|
||||
void beginFrame() override;
|
||||
/// Get started world program
|
||||
std::function<buffer::params(glm::mat4)> beginWorldPass();
|
||||
/// Get started entity program
|
||||
std::function<buffer::params(const std::vector<glm::mat4>&)> beginEntityPass();
|
||||
/// Draw cube indicator
|
||||
size_t drawIndicatorCube(glm::mat4 model);
|
||||
/// Apply postprocessing
|
||||
void endPass() override;
|
||||
void swapBuffer(GLFWwindow *) override;
|
||||
|
||||
void setClearColor(glm::vec4) override;
|
||||
|
||||
/// Apply camera matrices
|
||||
void lookFrom(const Camera&) override;
|
||||
void reloadShaders(const pass::VoxelProgram::options &);
|
||||
void reloadTextures(const std::string &, float mipMapLOD, float anisotropy);
|
||||
|
||||
private:
|
||||
GLuint VertexArrayID;
|
||||
|
||||
std::unique_ptr<pass::WorldProgram> WorldPass;
|
||||
std::unique_ptr<pass::EntityProgram> EntityPass;
|
||||
std::unique_ptr<pass::SkyProgram> SkyPass;
|
||||
std::unique_ptr<pass::ColorProgram> IndicatorPass;
|
||||
buffer::Colored IndicatorCubeBuffer;
|
||||
|
||||
glm::mat4 ProjectionMatrix;
|
||||
glm::mat4 ViewMatrix;
|
||||
|
||||
GLuint TextureAtlas;
|
||||
GLuint NormalAtlas;
|
||||
GLuint HOSAtlas;
|
||||
GLuint Skybox;
|
||||
|
||||
void loadTextures(const std::string &, float mipMapLOD, float anisotropy);
|
||||
void unloadTextures();
|
||||
};
|
||||
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
#include "Renderer.hpp"
|
||||
|
||||
#include "UI.hpp"
|
||||
#include "Pipeline.hpp"
|
||||
#include "../../../core/world/materials.hpp"
|
||||
#include "../../control/Camera.hpp"
|
||||
#include "../../Window.hpp"
|
||||
#include "../../../core/utils/logger.hpp"
|
||||
#include <GL/gl3w.h>
|
||||
|
||||
|
@ -12,11 +14,59 @@ using namespace render::gl;
|
|||
constexpr auto GL_MAJOR = 4;
|
||||
constexpr auto GL_MINOR = 6;
|
||||
|
||||
Renderer::Renderer(const Options& options): options(options) { }
|
||||
Renderer::~Renderer() { }
|
||||
Renderer::Renderer(const renderOptions& options):
|
||||
IndicatorCubeBuffer(GL_LINES, 24, {
|
||||
glm::vec3(0, 0, 0), glm::vec3(0, 0, 1),
|
||||
glm::vec3(0, 0, 1), glm::vec3(0, 1, 1),
|
||||
glm::vec3(0, 1, 1), glm::vec3(0, 1, 0),
|
||||
glm::vec3(0, 1, 0), glm::vec3(0, 0, 0),
|
||||
glm::vec3(1, 0, 0), glm::vec3(1, 0, 1),
|
||||
glm::vec3(1, 0, 1), glm::vec3(1, 1, 1),
|
||||
glm::vec3(1, 1, 1), glm::vec3(1, 1, 0),
|
||||
glm::vec3(1, 1, 0), glm::vec3(1, 0, 0),
|
||||
glm::vec3(0, 0, 0), glm::vec3(1, 0, 0),
|
||||
glm::vec3(0, 0, 1), glm::vec3(1, 0, 1),
|
||||
glm::vec3(0, 1, 1), glm::vec3(1, 1, 1),
|
||||
glm::vec3(0, 1, 0), glm::vec3(1, 1, 0),
|
||||
}, {
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
glm::vec4(1, 1, 1, 1), glm::vec4(1, 1, 1, 1),
|
||||
}) {
|
||||
glGenVertexArrays(1, &VertexArrayID);
|
||||
glBindVertexArray(VertexArrayID);
|
||||
|
||||
bool Renderer::Load(Window& window, const Options& options) {
|
||||
auto windowInfo = GetWindowInfo();
|
||||
reloadShaders(options.voxel);
|
||||
SkyPass = std::make_unique<pass::SkyProgram>();
|
||||
SkyEnable = options.skybox;
|
||||
IndicatorPass = std::make_unique<pass::ColorProgram>();
|
||||
|
||||
FogColor = glm::vec3(options.clear_color.x, options.clear_color.y, options.clear_color.z);
|
||||
loadTextures(options.textures, options.mipMapLOD, options.anisotropy);
|
||||
}
|
||||
|
||||
Renderer::~Renderer() {
|
||||
unloadTextures();
|
||||
glDeleteVertexArrays(1, &VertexArrayID);
|
||||
}
|
||||
|
||||
void framebuffer_size_callback(GLFWwindow *, int width, int height) {
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
|
||||
bool Renderer::Load(Window& window, const renderOptions& opt) {
|
||||
Window::CreateInfo windowInfo;
|
||||
windowInfo.pfnResize = framebuffer_size_callback;
|
||||
windowInfo.client = {Window::CreateInfo::Client::Type::GL, GL_MAJOR, GL_MINOR};
|
||||
windowInfo.samples = 1;
|
||||
if (!window.create(windowInfo))
|
||||
return false;
|
||||
|
@ -30,21 +80,7 @@ bool Renderer::Load(Window& window, const Options& options) {
|
|||
return false;
|
||||
}
|
||||
LOG_D("OpenGL " << glGetString(GL_VERSION) << ", GLSL " << glGetString(GL_SHADING_LANGUAGE_VERSION));
|
||||
sInstance = new Renderer(options);
|
||||
return true;
|
||||
}
|
||||
|
||||
void framebuffer_size_callback(GLFWwindow *, int width, int height) {
|
||||
glViewport(0, 0, width, height);
|
||||
}
|
||||
Window::CreateInfo Renderer::GetWindowInfo() {
|
||||
Window::CreateInfo windowInfo;
|
||||
windowInfo.pfnResize = framebuffer_size_callback;
|
||||
windowInfo.client = { Window::CreateInfo::Client::Type::GL, GL_MAJOR, GL_MINOR };
|
||||
return windowInfo;
|
||||
}
|
||||
|
||||
render::Pipeline *Renderer::createPipeline(const render::Pipeline::options &opt) {
|
||||
// Enable depth test
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
// Accept fragment if it closer to the camera than the former one
|
||||
|
@ -60,12 +96,95 @@ render::Pipeline *Renderer::createPipeline(const render::Pipeline::options &opt)
|
|||
glEnable(GL_MULTISAMPLE);
|
||||
}
|
||||
|
||||
glClearColor(options.clear_color.x, options.clear_color.y, options.clear_color.z, options.clear_color.w);
|
||||
glClearColor(opt.clear_color.x, opt.clear_color.y, opt.clear_color.z, opt.clear_color.w);
|
||||
|
||||
TracyGpuContext;
|
||||
return new Pipeline(opt);
|
||||
|
||||
sInstance = new Renderer(opt);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Renderer::loadUI(Window& w) {
|
||||
UI::Load(w);
|
||||
}
|
||||
|
||||
|
||||
void Renderer::beginFrame() {
|
||||
TracyGpuZone("Render");
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
|
||||
std::function<buffer::params(glm::mat4)> Renderer::beginWorldPass() {
|
||||
WorldPass->useIt();
|
||||
WorldPass->start(this);
|
||||
return [&](glm::mat4 model) {
|
||||
return WorldPass->setup(this, model);
|
||||
};
|
||||
}
|
||||
|
||||
std::function<buffer::params(const std::vector<glm::mat4> &)> Renderer::beginEntityPass() {
|
||||
EntityPass->useIt();
|
||||
EntityPass->start(this);
|
||||
return [&](const std::vector<glm::mat4>& models) {
|
||||
return EntityPass->setup(this, models);
|
||||
};
|
||||
}
|
||||
|
||||
size_t Renderer::drawIndicatorCube(glm::mat4 model) {
|
||||
IndicatorPass->useIt();
|
||||
return IndicatorCubeBuffer.draw(IndicatorPass->setup(this, model));
|
||||
}
|
||||
|
||||
void Renderer::endPass() {
|
||||
if(SkyEnable) {
|
||||
SkyPass->draw(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::swapBuffer(Window& w) {
|
||||
TracyGpuZone("Swap");
|
||||
glfwSwapBuffers(w.getPtr());
|
||||
TracyGpuCollect;
|
||||
}
|
||||
|
||||
void Renderer::reloadShaders(const pass::VoxelProgram::options& options) {
|
||||
WorldPass = std::make_unique<pass::WorldProgram>(options);
|
||||
EntityPass = std::make_unique<pass::EntityProgram>(options);
|
||||
}
|
||||
void Renderer::reloadTextures(const std::string& texturePath, float mipMapLOD, float anisotropy) {
|
||||
unloadTextures();
|
||||
loadTextures(texturePath, mipMapLOD, anisotropy);
|
||||
}
|
||||
|
||||
void Renderer::unloadTextures() {
|
||||
glDeleteTextures(1, &HOSAtlas);
|
||||
glDeleteTextures(1, &NormalAtlas);
|
||||
glDeleteTextures(1, &TextureAtlas);
|
||||
}
|
||||
void Renderer::loadTextures(const std::string& texturePath, float mipMapLOD, float anisotropy) {
|
||||
std::vector<std::string> terrainTextures;
|
||||
for(const auto& texture: world::materials::textures) {
|
||||
terrainTextures.emplace_back(texturePath + "/terrain/" + texture);
|
||||
}
|
||||
const auto ani = anisotropy >= 1 ? (1 << (static_cast<int>(anisotropy)-1)) : 0;
|
||||
TextureAtlas = pass::Program::loadTextureArray(terrainTextures, "", mipMapLOD, ani);
|
||||
NormalAtlas = pass::Program::loadTextureArray(terrainTextures, ".nrm", mipMapLOD, ani);
|
||||
HOSAtlas = pass::Program::loadTextureArray(terrainTextures, ".hos", mipMapLOD, ani);
|
||||
|
||||
Skybox = pass::Program::loadTextureCube(texturePath + "/sky/Space_tray");
|
||||
}
|
||||
|
||||
void Renderer::lookFrom(const Camera& camera) {
|
||||
ProjectionMatrix = camera.getProjectionMatrix();
|
||||
ViewMatrix = camera.getViewMatrix();
|
||||
FogDepth = camera.getDepth();
|
||||
}
|
||||
|
||||
void Renderer::setClearColor(glm::vec4 c) {
|
||||
FogColor = c;
|
||||
glClearColor(c.r, c.g, c.b, c.a);
|
||||
}
|
||||
void Renderer::setCurvature(glm::vec4 sp, float c) {
|
||||
SphereProj = sp;
|
||||
Curvature = c;
|
||||
}
|
|
@ -1,7 +1,12 @@
|
|||
#pragma once
|
||||
#include "../Renderer.hpp"
|
||||
|
||||
#include <glm/vec4.hpp>
|
||||
#include <GL/gl3w.h>
|
||||
#include <memory>
|
||||
#include "pass/WorldProgram.hpp"
|
||||
#include "pass/EntityProgram.hpp"
|
||||
#include "pass/SkyProgram.hpp"
|
||||
#include "pass/ColorProgram.hpp"
|
||||
#include "buffer/Colored.hpp"
|
||||
|
||||
namespace render::gl {
|
||||
|
||||
|
@ -10,23 +15,85 @@ class Renderer final: public render::Renderer {
|
|||
public:
|
||||
virtual ~Renderer();
|
||||
|
||||
struct Options {
|
||||
/// Depth color
|
||||
glm::vec4 clear_color;
|
||||
};
|
||||
static bool Load(Window& window, const renderOptions& options);
|
||||
|
||||
static bool Load(Window& window, const Options &options);
|
||||
glm::vec3 FogColor;
|
||||
GLfloat FogDepth;
|
||||
|
||||
render::Pipeline *createPipeline(const render::Pipeline::options &) override;
|
||||
glm::mat4 getProjectionMatrix() const {
|
||||
return ProjectionMatrix;
|
||||
}
|
||||
glm::mat4 getViewMatrix() const {
|
||||
return ViewMatrix;
|
||||
}
|
||||
|
||||
GLuint getTextureAtlas() const {
|
||||
return TextureAtlas;
|
||||
}
|
||||
GLuint getNormalAtlas() const {
|
||||
return NormalAtlas;
|
||||
}
|
||||
GLuint getHOSAtlas() const {
|
||||
return HOSAtlas;
|
||||
}
|
||||
GLuint getSkyTexture() const {
|
||||
return Skybox;
|
||||
}
|
||||
|
||||
void beginFrame() override;
|
||||
std::function<buffer::params(glm::mat4)> beginWorldPass() override;
|
||||
std::function<buffer::params(const std::vector<glm::mat4>&)> beginEntityPass() override;
|
||||
size_t drawIndicatorCube(glm::mat4 model) override;
|
||||
void endPass() override;
|
||||
void swapBuffer(Window&) override;
|
||||
|
||||
void setClearColor(glm::vec4) override;
|
||||
void setCurvature(glm::vec4, float) override;
|
||||
glm::vec4 getSphereProj() const {
|
||||
return SphereProj;
|
||||
}
|
||||
float getCurvature() const {
|
||||
return Curvature;
|
||||
}
|
||||
|
||||
/// Apply camera matrices
|
||||
void lookFrom(const Camera&) override;
|
||||
void reloadShaders(const pass::VoxelProgram::options &) override;
|
||||
void reloadTextures(const std::string &, float mipMapLOD, float anisotropy) override;
|
||||
|
||||
void loadUI(Window &) override;
|
||||
|
||||
static _FORCE_INLINE_ Renderer *Get() { return static_cast<Renderer*>(render::Renderer::Get()); }
|
||||
|
||||
private:
|
||||
Renderer(const Options &options);
|
||||
static Window::CreateInfo GetWindowInfo();
|
||||
Renderer(const renderOptions &options);
|
||||
|
||||
Options options;
|
||||
GLuint VertexArrayID;
|
||||
|
||||
std::unique_ptr<pass::WorldProgram> WorldPass;
|
||||
std::unique_ptr<pass::EntityProgram> EntityPass;
|
||||
std::unique_ptr<pass::SkyProgram> SkyPass;
|
||||
std::unique_ptr<pass::ColorProgram> IndicatorPass;
|
||||
buffer::Colored IndicatorCubeBuffer;
|
||||
|
||||
glm::mat4 ProjectionMatrix;
|
||||
glm::mat4 ViewMatrix;
|
||||
|
||||
GLuint TextureAtlas;
|
||||
GLuint NormalAtlas;
|
||||
GLuint HOSAtlas;
|
||||
GLuint Skybox;
|
||||
|
||||
/// Sphere bending
|
||||
/// offset.xyz radius.w
|
||||
glm::vec4 SphereProj;
|
||||
/// Ratio between spherical and cartesian
|
||||
float Curvature;
|
||||
|
||||
/// Draw skybox
|
||||
bool SkyEnable;
|
||||
|
||||
void loadTextures(const std::string &, float mipMapLOD, float anisotropy);
|
||||
void unloadTextures();
|
||||
};
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
#include "UI.hpp"
|
||||
|
||||
#include "../../Window.hpp"
|
||||
#include <GL/gl3w.h>
|
||||
#include <imgui_impl_glfw.h>
|
||||
#include <imgui_impl_opengl3.h>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "../UI.hpp"
|
||||
#include "../../Window.hpp"
|
||||
struct GLFWwindow;
|
||||
class Window;
|
||||
|
||||
namespace render::gl {
|
||||
|
||||
|
|
|
@ -1,17 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <GL/gl3w.h>
|
||||
#include <sys/types.h>
|
||||
#include "../../buffer/Abstract.hpp"
|
||||
|
||||
namespace buffer {
|
||||
/// Draw options
|
||||
struct params {
|
||||
/// Bind only vertices positions
|
||||
bool vertexOnly;
|
||||
/// Draw instanced model
|
||||
size_t instances = 1;
|
||||
};
|
||||
|
||||
/// Abstract OpenGL Buffer
|
||||
class Abstract {
|
||||
public:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "ColorProgram.hpp"
|
||||
|
||||
#include "../Pipeline.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
|
||||
using namespace pass;
|
||||
|
||||
|
@ -20,7 +20,7 @@ ColorProgram::~ColorProgram() { }
|
|||
std::string ColorProgram::getName() const {
|
||||
return "Color";
|
||||
}
|
||||
buffer::params ColorProgram::setup(render::gl::Pipeline *renderer, glm::mat4 modelMatrix) {
|
||||
buffer::params ColorProgram::setup(render::gl::Renderer *renderer, glm::mat4 modelMatrix) {
|
||||
const auto mvp = renderer->getProjectionMatrix() * renderer->getViewMatrix() * modelMatrix;
|
||||
setMVP(&mvp[0][0]);
|
||||
return buffer::params{.vertexOnly = false};
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace pass {
|
|||
ColorProgram();
|
||||
~ColorProgram();
|
||||
|
||||
buffer::params setup(render::gl::Pipeline *, glm::mat4 modelMatrix);
|
||||
buffer::params setup(render::gl::Renderer *, glm::mat4 modelMatrix);
|
||||
|
||||
protected:
|
||||
std::string getName() const override;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "EntityProgram.hpp"
|
||||
|
||||
#include "../Pipeline.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
|
||||
using namespace pass;
|
||||
|
||||
|
@ -17,7 +17,7 @@ EntityProgram::EntityProgram(const EntityProgram::options &opts) : VoxelProgram(
|
|||
}
|
||||
EntityProgram::~EntityProgram() { }
|
||||
|
||||
buffer::params EntityProgram::setup(render::gl::Pipeline *renderer, const std::vector<glm::mat4>& modelsMatrices) {
|
||||
buffer::params EntityProgram::setup(render::gl::Renderer *renderer, const std::vector<glm::mat4>& modelsMatrices) {
|
||||
setModels(&modelsMatrices[0][0][0], modelsMatrices.size());
|
||||
auto params = VoxelProgram::setup(renderer);
|
||||
params.instances = modelsMatrices.size();
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace pass {
|
|||
|
||||
static constexpr auto LOCATION = 6;
|
||||
|
||||
buffer::params setup(render::gl::Pipeline *, const std::vector<glm::mat4> &modelsMatrices);
|
||||
buffer::params setup(render::gl::Renderer *, const std::vector<glm::mat4> &modelsMatrices);
|
||||
void disable();
|
||||
|
||||
protected:
|
||||
|
|
|
@ -8,8 +8,11 @@
|
|||
#include "Shader.hpp"
|
||||
#include "../buffer/Abstract.hpp"
|
||||
|
||||
namespace render {
|
||||
struct passOptions;
|
||||
}
|
||||
namespace render::gl {
|
||||
class Pipeline;
|
||||
class Renderer;
|
||||
}
|
||||
/// OpenGL programs
|
||||
namespace pass {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "SkyProgram.hpp"
|
||||
|
||||
#include "../Pipeline.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
|
||||
using namespace pass;
|
||||
|
||||
|
@ -65,7 +65,7 @@ SkyProgram::~SkyProgram() { }
|
|||
std::string SkyProgram::getName() const {
|
||||
return "Sky";
|
||||
}
|
||||
void SkyProgram::start(render::gl::Pipeline *renderer) {
|
||||
void SkyProgram::start(render::gl::Renderer *renderer) {
|
||||
const auto fixedView = glm::mat4(glm::mat3(renderer->getViewMatrix()));
|
||||
setView(&fixedView[0][0]);
|
||||
setProjection(&renderer->getProjectionMatrix()[0][0]);
|
||||
|
@ -75,7 +75,7 @@ void SkyProgram::start(render::gl::Pipeline *renderer) {
|
|||
buffer::params SkyProgram::setup() {
|
||||
return buffer::params{.vertexOnly = true};
|
||||
}
|
||||
void SkyProgram::draw(render::gl::Pipeline *renderer) {
|
||||
void SkyProgram::draw(render::gl::Renderer *renderer) {
|
||||
useIt();
|
||||
start(renderer);
|
||||
CubeBuffer.draw(setup());
|
||||
|
|
|
@ -10,11 +10,11 @@ namespace pass {
|
|||
SkyProgram();
|
||||
~SkyProgram();
|
||||
|
||||
void start(render::gl::Pipeline *);
|
||||
void start(render::gl::Renderer *);
|
||||
buffer::params setup();
|
||||
|
||||
/// Direct draw using internal buffer
|
||||
void draw(render::gl::Pipeline *);
|
||||
void draw(render::gl::Renderer *);
|
||||
|
||||
protected:
|
||||
std::string getName() const override;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "VoxelProgram.hpp"
|
||||
|
||||
#include "../Pipeline.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
|
||||
using namespace pass;
|
||||
|
||||
|
@ -53,7 +53,7 @@ VoxelProgram::~VoxelProgram() { }
|
|||
std::string VoxelProgram::getName() const {
|
||||
return "Voxel";
|
||||
}
|
||||
void VoxelProgram::start(render::gl::Pipeline *renderer) {
|
||||
void VoxelProgram::start(render::gl::Renderer *renderer) {
|
||||
bindTexture(renderer->getTextureAtlas());
|
||||
bindNormal(renderer->getNormalAtlas());
|
||||
bindHOS(renderer->getHOSAtlas());
|
||||
|
@ -62,9 +62,9 @@ void VoxelProgram::start(render::gl::Pipeline *renderer) {
|
|||
setView(&renderer->getViewMatrix()[0][0]);
|
||||
setProj(&renderer->getProjectionMatrix()[0][0]);
|
||||
}
|
||||
buffer::params VoxelProgram::setup(render::gl::Pipeline *renderer) {
|
||||
setSphereProj(&renderer->SphereProj[0]);
|
||||
setCurvature(renderer->Curvature);
|
||||
buffer::params VoxelProgram::setup(render::gl::Renderer *renderer) {
|
||||
setSphereProj(&renderer->getSphereProj()[0]);
|
||||
setCurvature(renderer->getCurvature());
|
||||
return buffer::params{.vertexOnly = false};
|
||||
}
|
||||
|
||||
|
|
|
@ -6,33 +6,15 @@ namespace pass {
|
|||
/// Abstract voxels pass
|
||||
class VoxelProgram: public Program {
|
||||
public:
|
||||
/// Pass options
|
||||
struct options {
|
||||
/// Apply light properties
|
||||
bool pbr = true;
|
||||
/// Triplanar texture mapping
|
||||
bool triplanar = false;
|
||||
/// Transform texture UV
|
||||
bool stochastic = false;
|
||||
/// Active geometry pass
|
||||
bool geometry = false;
|
||||
/// Blend voxel with mixed materials (requires geometry)
|
||||
bool blend = true;
|
||||
/// Depth fog
|
||||
bool fog = true;
|
||||
/// Map planets to sphere
|
||||
bool curvature = true;
|
||||
/// Keep depth in sphere
|
||||
bool curv_depth = true;
|
||||
};
|
||||
using options = render::passOptions;
|
||||
|
||||
VoxelProgram(const options &opts, std::vector<std::string> flags = {});
|
||||
~VoxelProgram();
|
||||
|
||||
void start(render::gl::Pipeline *);
|
||||
void start(render::gl::Renderer *);
|
||||
|
||||
protected:
|
||||
buffer::params setup(render::gl::Pipeline *);
|
||||
buffer::params setup(render::gl::Renderer *);
|
||||
|
||||
std::string getName() const override;
|
||||
void setView(const GLfloat *matrix);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "WorldProgram.hpp"
|
||||
|
||||
#include "../Pipeline.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
|
||||
using namespace pass;
|
||||
|
||||
|
@ -11,7 +11,7 @@ WorldProgram::WorldProgram(const WorldProgram::options& opts): VoxelProgram(opts
|
|||
|
||||
WorldProgram::~WorldProgram() { }
|
||||
|
||||
buffer::params WorldProgram::setup(render::gl::Pipeline *renderer, glm::mat4 modelMatrix) {
|
||||
buffer::params WorldProgram::setup(render::gl::Renderer *renderer, glm::mat4 modelMatrix) {
|
||||
setModel(&modelMatrix[0][0]);
|
||||
return VoxelProgram::setup(renderer);
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace pass {
|
|||
WorldProgram(const options &opts);
|
||||
~WorldProgram();
|
||||
|
||||
buffer::params setup(render::gl::Pipeline *, glm::mat4 modelMatrix);
|
||||
buffer::params setup(render::gl::Renderer *, glm::mat4 modelMatrix);
|
||||
|
||||
protected:
|
||||
void setModel(const GLfloat *matrix);
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
#include "gl/Renderer.hpp"
|
||||
#include "vk/Renderer.hpp"
|
||||
#include "../../core/utils/logger.hpp"
|
||||
#include "../Window.hpp"
|
||||
|
||||
namespace render {
|
||||
|
||||
bool Load(Window& window, bool preferVulkan, const renderOptions& options) {
|
||||
if(!preferVulkan) {
|
||||
LOG_D("Trying OpenGL");
|
||||
if(gl::Renderer::Load(window, options))
|
||||
return true;
|
||||
window.destroy();
|
||||
}
|
||||
LOG_D("Trying Vulkan");
|
||||
if(vk::Renderer::Load(window, options))
|
||||
return true;
|
||||
window.destroy();
|
||||
if(preferVulkan) {
|
||||
LOG_I("Fallback to OpenGL");
|
||||
if(gl::Renderer::Load(window, options))
|
||||
return true;
|
||||
}
|
||||
LOG_E("No available graphics library");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
class Window;
|
||||
namespace render {
|
||||
struct renderOptions;
|
||||
|
||||
bool Load(Window& window, bool preferVulkan, const renderOptions& options);
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
#include "CommandPool.hpp"
|
||||
|
||||
#include "shared.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
|
||||
using namespace render::vk;
|
||||
|
||||
CommandPool::CommandPool(VkDevice device, const std::vector<VkImageView>& views, PipelineRef pipe, const PhysicalDeviceInfo& info, const renderOptions& opt):
|
||||
device(device) {
|
||||
|
||||
VkCommandPoolCreateInfo poolInfo{};
|
||||
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
poolInfo.queueFamilyIndex = info.queueIndices.graphicsFamily.value();
|
||||
poolInfo.flags = 0; // Optional
|
||||
|
||||
if (vkCreateCommandPool(device, &poolInfo, ALLOC, &graphicsPool) != VK_SUCCESS) {
|
||||
FATAL("Failed to create command pool!");
|
||||
}
|
||||
|
||||
allocate(views, pipe, info.swapDetails.capabilities.currentExtent, opt);
|
||||
}
|
||||
CommandPool::~CommandPool() {
|
||||
if(!freed)
|
||||
free();
|
||||
|
||||
vkDestroyCommandPool(device, graphicsPool, ALLOC);
|
||||
}
|
||||
void CommandPool::allocate(const std::vector<VkImageView>& views, PipelineRef pipe, VkExtent2D extent, const renderOptions& opt) {
|
||||
assert(freed);
|
||||
|
||||
framebuffers.resize(views.size());
|
||||
|
||||
for (size_t i = 0; i < views.size(); i++) {
|
||||
VkImageView attachments[] = { views[i] };
|
||||
|
||||
VkFramebufferCreateInfo framebufferInfo{};
|
||||
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||
framebufferInfo.renderPass = pipe.first;
|
||||
framebufferInfo.attachmentCount = 1;
|
||||
framebufferInfo.pAttachments = attachments;
|
||||
framebufferInfo.width = extent.width;
|
||||
framebufferInfo.height = extent.height;
|
||||
framebufferInfo.layers = 1;
|
||||
|
||||
if (vkCreateFramebuffer(device, &framebufferInfo, ALLOC, &framebuffers[i]) != VK_SUCCESS) {
|
||||
FATAL("Failed to create framebuffer!");
|
||||
}
|
||||
}
|
||||
|
||||
graphicsBuffers.resize(framebuffers.size());
|
||||
|
||||
VkCommandBufferAllocateInfo allocInfo{};
|
||||
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
allocInfo.commandPool = graphicsPool;
|
||||
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
allocInfo.commandBufferCount = (uint32_t) graphicsBuffers.size();
|
||||
|
||||
if (vkAllocateCommandBuffers(device, &allocInfo, graphicsBuffers.data()) != VK_SUCCESS) {
|
||||
FATAL("Failed to allocate command buffers!");
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < graphicsBuffers.size(); i++) {
|
||||
VkCommandBufferBeginInfo beginInfo{};
|
||||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
beginInfo.flags = 0;
|
||||
beginInfo.pInheritanceInfo = nullptr;
|
||||
|
||||
if (vkBeginCommandBuffer(graphicsBuffers[i], &beginInfo) != VK_SUCCESS) {
|
||||
FATAL("Failed to begin recording command buffer!");
|
||||
}
|
||||
|
||||
VkRenderPassBeginInfo renderPassInfo{};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
renderPassInfo.renderPass = pipe.first;
|
||||
renderPassInfo.framebuffer = framebuffers[i];
|
||||
renderPassInfo.renderArea.offset = {0, 0};
|
||||
renderPassInfo.renderArea.extent = extent;
|
||||
|
||||
VkClearValue clearColor = {opt.clear_color.x, opt.clear_color.y, opt.clear_color.z, opt.clear_color.a};
|
||||
renderPassInfo.clearValueCount = 1; //TODO: clear depth
|
||||
renderPassInfo.pClearValues = &clearColor;
|
||||
|
||||
vkCmdBeginRenderPass(graphicsBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
vkCmdBindPipeline(graphicsBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipe.second);
|
||||
vkCmdDraw(graphicsBuffers[i], 3, 1, 0, 0);
|
||||
vkCmdEndRenderPass(graphicsBuffers[i]);
|
||||
|
||||
if (vkEndCommandBuffer(graphicsBuffers[i]) != VK_SUCCESS) {
|
||||
FATAL("Failed to record command buffer!");
|
||||
}
|
||||
}
|
||||
|
||||
freed = false;
|
||||
}
|
||||
void CommandPool::free() {
|
||||
assert(!freed);
|
||||
for (size_t i = 0; i < framebuffers.size(); i++) {
|
||||
vkDestroyFramebuffer(device, framebuffers[i], nullptr);
|
||||
}
|
||||
|
||||
vkFreeCommandBuffers(device, graphicsPool, static_cast<uint32_t>(graphicsBuffers.size()), graphicsBuffers.data());
|
||||
|
||||
freed = true;
|
||||
}
|
||||
|
||||
void CommandPool::submitGraphics(uint32_t idx, VkQueue graphicsQueue, VkSemaphore waitSemaphore, VkSemaphore signalSemaphore, VkFence submittedFence) {
|
||||
assert(!freed);
|
||||
|
||||
VkSubmitInfo submitInfo{};
|
||||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
|
||||
VkSemaphore waitSemaphores[] = {waitSemaphore};
|
||||
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
||||
submitInfo.waitSemaphoreCount = 1;
|
||||
submitInfo.pWaitSemaphores = waitSemaphores;
|
||||
submitInfo.pWaitDstStageMask = waitStages;
|
||||
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &graphicsBuffers[idx];
|
||||
|
||||
VkSemaphore signalSemaphores[] = {signalSemaphore};
|
||||
submitInfo.signalSemaphoreCount = 1;
|
||||
submitInfo.pSignalSemaphores = signalSemaphores;
|
||||
|
||||
vkResetFences(device, 1, &submittedFence);
|
||||
if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, submittedFence) != VK_SUCCESS) {
|
||||
FATAL("Failed to submit draw command buffer!");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "forward.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace render::vk {
|
||||
class SwapChain;
|
||||
class Pipeline;
|
||||
|
||||
class CommandPool {
|
||||
public:
|
||||
CommandPool(VkDevice, const std::vector<VkImageView>&, PipelineRef, const PhysicalDeviceInfo&, const renderOptions&);
|
||||
~CommandPool();
|
||||
|
||||
void submitGraphics(uint32_t, VkQueue, VkSemaphore, VkSemaphore, VkFence);
|
||||
|
||||
void allocate(const std::vector<VkImageView>&, PipelineRef, VkExtent2D, const renderOptions&);
|
||||
void free();
|
||||
|
||||
private:
|
||||
VkDevice device;
|
||||
|
||||
std::vector<VkFramebuffer> framebuffers;
|
||||
|
||||
VkCommandPool graphicsPool;
|
||||
std::vector<VkCommandBuffer> graphicsBuffers;
|
||||
bool freed = true;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
#include "Pipeline.hpp"
|
||||
|
||||
#include "shared.hpp"
|
||||
#include "../../../core/data/file.hpp"
|
||||
#include "../Renderer.hpp"
|
||||
|
||||
#define CONTENT_DIR "content/"
|
||||
#define SHADER_DIR CONTENT_DIR "shaders/"
|
||||
#define TEXTURES_DIR CONTENT_DIR "textures/"
|
||||
|
||||
constexpr auto BLENDING = true;
|
||||
|
||||
using namespace render::vk;
|
||||
|
||||
Pipeline::Pipeline(VkDevice device, const PhysicalDeviceInfo &info, const renderOptions &options): device(device) {
|
||||
{ // Render pass
|
||||
VkAttachmentDescription colorAttachment{};
|
||||
colorAttachment.format = info.surfaceFormat.format;
|
||||
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
|
||||
//TODO: subpasses (world, entities, colors, sky, ui)
|
||||
VkAttachmentReference colorAttachmentRef{};
|
||||
colorAttachmentRef.attachment = 0; //TODO: layouts bind
|
||||
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
VkSubpassDescription subpass{};
|
||||
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
|
||||
subpass.colorAttachmentCount = 1;
|
||||
subpass.pColorAttachments = &colorAttachmentRef;
|
||||
|
||||
VkSubpassDependency dependency{};
|
||||
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
|
||||
dependency.dstSubpass = 0;
|
||||
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.srcAccessMask = 0;
|
||||
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
|
||||
VkRenderPassCreateInfo renderPassInfo{};
|
||||
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
renderPassInfo.attachmentCount = 1;
|
||||
renderPassInfo.pAttachments = &colorAttachment;
|
||||
renderPassInfo.subpassCount = 1;
|
||||
renderPassInfo.pSubpasses = &subpass;
|
||||
renderPassInfo.dependencyCount = 1;
|
||||
renderPassInfo.pDependencies = &dependency;
|
||||
|
||||
if (vkCreateRenderPass(device, &renderPassInfo, ALLOC, &renderPass) != VK_SUCCESS) {
|
||||
FATAL("Failed to create render pass!");
|
||||
}
|
||||
}
|
||||
{
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
|
||||
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
pipelineLayoutInfo.setLayoutCount = 0; // TODO: setup layouts
|
||||
pipelineLayoutInfo.pSetLayouts = nullptr;
|
||||
pipelineLayoutInfo.pushConstantRangeCount = 0;
|
||||
pipelineLayoutInfo.pPushConstantRanges = nullptr;
|
||||
|
||||
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, ALLOC, &pipelineLayout) != VK_SUCCESS) {
|
||||
FATAL("Failed to create pipeline layout!");
|
||||
}
|
||||
}
|
||||
{ // Pipeline
|
||||
{ // Modules
|
||||
data::file_content vsFile({SHADER_DIR "Tris.vs.spv"});
|
||||
data::file_content fsFile({SHADER_DIR "Tris.fs.spv"});
|
||||
|
||||
auto createShaderModule = [&](const data::file_content &code) {
|
||||
VkShaderModuleCreateInfo createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
createInfo.codeSize = code.size();
|
||||
createInfo.pCode = reinterpret_cast<const uint32_t *>(code.data());
|
||||
|
||||
VkShaderModule shaderModule;
|
||||
if (vkCreateShaderModule(device, &createInfo, ALLOC, &shaderModule) != VK_SUCCESS) {
|
||||
FATAL("Failed to create shader module!");
|
||||
}
|
||||
return shaderModule;
|
||||
};
|
||||
|
||||
vsShader = createShaderModule(vsFile);
|
||||
fsShader = createShaderModule(fsFile);
|
||||
}
|
||||
|
||||
VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
|
||||
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vertShaderStageInfo.module = vsShader;
|
||||
vertShaderStageInfo.pName = "main";
|
||||
vertShaderStageInfo.pSpecializationInfo = nullptr; //TODO: pass constants
|
||||
|
||||
VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
|
||||
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fragShaderStageInfo.module = fsShader;
|
||||
fragShaderStageInfo.pName = "main";
|
||||
fragShaderStageInfo.pSpecializationInfo = nullptr; //TODO: pass constants
|
||||
|
||||
//TODO: geometry
|
||||
|
||||
VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
|
||||
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
vertexInputInfo.vertexBindingDescriptionCount = 0;
|
||||
vertexInputInfo.pVertexBindingDescriptions = nullptr; // TODO: uniforms, and buffers
|
||||
vertexInputInfo.vertexAttributeDescriptionCount = 0;
|
||||
vertexInputInfo.pVertexAttributeDescriptions = nullptr; // TODO: uniforms, and buffers
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
|
||||
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
inputAssembly.primitiveRestartEnable = VK_FALSE;
|
||||
|
||||
// Viewport
|
||||
VkViewport viewport{};
|
||||
viewport.x = 0.0f;
|
||||
viewport.y = 0.0f;
|
||||
viewport.width = (float)info.swapDetails.capabilities.currentExtent.width;
|
||||
viewport.height = (float)info.swapDetails.capabilities.currentExtent.height;
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
|
||||
VkRect2D scissor{};
|
||||
scissor.offset = {0, 0};
|
||||
scissor.extent = info.swapDetails.capabilities.currentExtent;
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewportState{};
|
||||
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
viewportState.viewportCount = 1;
|
||||
viewportState.pViewports = &viewport;
|
||||
viewportState.scissorCount = 1;
|
||||
viewportState.pScissors = &scissor;
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterizer{};
|
||||
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
rasterizer.depthClampEnable = VK_FALSE;
|
||||
rasterizer.rasterizerDiscardEnable = VK_FALSE;
|
||||
rasterizer.polygonMode = options.wireframe ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL;
|
||||
rasterizer.lineWidth = 1.0f;
|
||||
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
|
||||
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
||||
rasterizer.depthBiasEnable = VK_FALSE;
|
||||
rasterizer.depthBiasConstantFactor = 0.0f;
|
||||
rasterizer.depthBiasClamp = 0.0f;
|
||||
rasterizer.depthBiasSlopeFactor = 0.0f;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisampling{}; //TODO: multisampling NOTE: requires gpu feature
|
||||
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
multisampling.sampleShadingEnable = VK_FALSE;
|
||||
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||
multisampling.minSampleShading = 1.0f;
|
||||
multisampling.pSampleMask = nullptr;
|
||||
multisampling.alphaToCoverageEnable = VK_FALSE;
|
||||
multisampling.alphaToOneEnable = VK_FALSE;
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo depthStencil{};
|
||||
depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
depthStencil.stencilTestEnable = VK_FALSE;
|
||||
depthStencil.depthTestEnable = VK_FALSE; // TODO: depth filter NOTE: set as null if create info
|
||||
|
||||
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
|
||||
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
||||
if constexpr (BLENDING) {
|
||||
colorBlendAttachment.blendEnable = VK_TRUE;
|
||||
colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
|
||||
colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||
colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
|
||||
colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||
colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
|
||||
colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||
} else {
|
||||
colorBlendAttachment.blendEnable = VK_FALSE;
|
||||
}
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo colorBlending{};
|
||||
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
colorBlending.attachmentCount = 1; //MAYBE: deferred multitarget
|
||||
colorBlending.pAttachments = &colorBlendAttachment;
|
||||
colorBlending.logicOpEnable = VK_FALSE;
|
||||
colorBlending.logicOp = VK_LOGIC_OP_COPY;
|
||||
colorBlending.blendConstants[0] = 0.0f;
|
||||
colorBlending.blendConstants[1] = 0.0f;
|
||||
colorBlending.blendConstants[2] = 0.0f;
|
||||
colorBlending.blendConstants[3] = 0.0f;
|
||||
|
||||
/* MAYBE: dynamic state
|
||||
VkDynamicState dynamicStates[] = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_LINE_WIDTH};
|
||||
|
||||
VkPipelineDynamicStateCreateInfo dynamicState{};
|
||||
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
dynamicState.dynamicStateCount = 2;
|
||||
dynamicState.pDynamicStates = dynamicStates;
|
||||
*/
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipelineInfo{};
|
||||
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipelineInfo.stageCount = 2;
|
||||
pipelineInfo.pStages = shaderStages;
|
||||
pipelineInfo.pVertexInputState = &vertexInputInfo;
|
||||
pipelineInfo.pInputAssemblyState = &inputAssembly;
|
||||
pipelineInfo.pViewportState = &viewportState;
|
||||
pipelineInfo.pRasterizationState = &rasterizer;
|
||||
pipelineInfo.pMultisampleState = &multisampling;
|
||||
pipelineInfo.pDepthStencilState = nullptr; //FIXME: depthTest
|
||||
pipelineInfo.pColorBlendState = &colorBlending;
|
||||
pipelineInfo.pDynamicState = nullptr;
|
||||
|
||||
pipelineInfo.layout = pipelineLayout;
|
||||
pipelineInfo.renderPass = renderPass;
|
||||
pipelineInfo.subpass = 0;
|
||||
|
||||
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
|
||||
pipelineInfo.basePipelineIndex = -1;
|
||||
|
||||
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, ALLOC, &graphicsPipeline) != VK_SUCCESS) {
|
||||
FATAL("Failed to create graphics pipeline!");
|
||||
}
|
||||
}
|
||||
}
|
||||
Pipeline::~Pipeline() {
|
||||
vkDestroyPipeline(device, graphicsPipeline, ALLOC);
|
||||
vkDestroyPipelineLayout(device, pipelineLayout, ALLOC);
|
||||
vkDestroyRenderPass(device, renderPass, ALLOC);
|
||||
|
||||
vkDestroyShaderModule(device, fsShader, ALLOC);
|
||||
vkDestroyShaderModule(device, vsShader, ALLOC);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include "forward.hpp"
|
||||
|
||||
namespace render::vk {
|
||||
|
||||
class Pipeline {
|
||||
public:
|
||||
Pipeline(VkDevice, const PhysicalDeviceInfo&, const renderOptions&);
|
||||
~Pipeline();
|
||||
|
||||
PipelineRef getRef() { return std::make_pair(renderPass, graphicsPipeline); }
|
||||
|
||||
private:
|
||||
VkDevice device;
|
||||
|
||||
VkShaderModule vsShader;
|
||||
VkShaderModule fsShader;
|
||||
|
||||
VkRenderPass renderPass;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
VkPipeline graphicsPipeline;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,476 @@
|
|||
#include "Renderer.hpp"
|
||||
|
||||
#include "UI.hpp"
|
||||
#include "../../Window.hpp"
|
||||
#include "shared.hpp"
|
||||
#include "SwapChain.hpp"
|
||||
#include "Pipeline.hpp"
|
||||
#include "CommandPool.hpp"
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
using namespace render::vk;
|
||||
|
||||
constexpr auto LOAD_DEVICE = true;
|
||||
constexpr auto VALIDATION_LAYER = true;
|
||||
|
||||
void set_current_extent(VkSurfaceCapabilitiesKHR &capabilities, GLFWwindow *ptr);
|
||||
|
||||
Renderer::Renderer(VkInstance instance, VkDevice device, const PhysicalDeviceInfo& info, const renderOptions& opt):
|
||||
options(opt), instance(instance), surface(info.surface), device(device),
|
||||
physicalInfo(std::make_unique<PhysicalDeviceInfo>(info)) {
|
||||
vkGetDeviceQueue(device, info.queueIndices.graphicsFamily.value(), 0, &graphicsQueue);
|
||||
vkGetDeviceQueue(device, info.queueIndices.presentFamily.value(), 0, &presentQueue);
|
||||
|
||||
set_current_extent(physicalInfo->swapDetails.capabilities, physicalInfo->window);
|
||||
physicalInfo->surfaceFormat = [&]() {
|
||||
for(const auto& format: info.swapDetails.formats) {
|
||||
if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_W("Using suboptimal surface format");
|
||||
return info.swapDetails.formats[0];
|
||||
}();
|
||||
|
||||
swapChain = std::make_unique<SwapChain>(device, *physicalInfo.get());
|
||||
pipeline = std::make_unique<Pipeline>(device, *physicalInfo.get(), options);
|
||||
commandPool = std::make_unique<CommandPool>(device, swapChain->getImageViews(), pipeline->getRef(), *physicalInfo.get(), options);
|
||||
|
||||
{
|
||||
imageAvailableSemaphores.resize(opt.inFlightFrames);
|
||||
renderFinishedSemaphores.resize(opt.inFlightFrames);
|
||||
inFlightFences.resize(opt.inFlightFrames);
|
||||
|
||||
VkSemaphoreCreateInfo semaphoreInfo{};
|
||||
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
|
||||
VkFenceCreateInfo fenceInfo{};
|
||||
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||||
|
||||
for (int i = 0; i < opt.inFlightFrames; i++) {
|
||||
if (vkCreateSemaphore(device, &semaphoreInfo, ALLOC, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
|
||||
vkCreateSemaphore(device, &semaphoreInfo, ALLOC, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
|
||||
vkCreateFence(device, &fenceInfo, ALLOC, &inFlightFences[i]) != VK_SUCCESS)
|
||||
{
|
||||
FATAL("Failed to create synchronization objects!");
|
||||
}
|
||||
}
|
||||
vkResetFences(device, 1, &inFlightFences[currentFrame]);
|
||||
}
|
||||
}
|
||||
Renderer::~Renderer() {
|
||||
vkDeviceWaitIdle(device);
|
||||
destroySwapChain();
|
||||
|
||||
for(size_t i = 0; i < renderFinishedSemaphores.size(); i++) {
|
||||
vkDestroyFence(device, inFlightFences[i], ALLOC);
|
||||
vkDestroySemaphore(device, renderFinishedSemaphores[i], ALLOC);
|
||||
vkDestroySemaphore(device, imageAvailableSemaphores[i], ALLOC);
|
||||
}
|
||||
|
||||
commandPool.reset();
|
||||
|
||||
vkDestroyDevice(device, ALLOC);
|
||||
vkDestroySurfaceKHR(instance, surface, ALLOC);
|
||||
vkDestroyInstance(instance, ALLOC);
|
||||
}
|
||||
|
||||
void Renderer::recreateSwapChain() {
|
||||
LOG_D("Recreating swapchain");
|
||||
vkDeviceWaitIdle(device);
|
||||
destroySwapChain();
|
||||
|
||||
physicalInfo->swapDetails = SwapChainSupportDetails::Query(physicalInfo->device, physicalInfo->surface);
|
||||
set_current_extent(physicalInfo->swapDetails.capabilities, physicalInfo->window);
|
||||
swapChain = std::make_unique<SwapChain>(device, *physicalInfo.get());
|
||||
pipeline = std::make_unique<Pipeline>(device, *physicalInfo.get(), options);
|
||||
commandPool->allocate(swapChain->getImageViews(), pipeline->getRef(), physicalInfo->swapDetails.capabilities.currentExtent, options);
|
||||
}
|
||||
void Renderer::destroySwapChain() {
|
||||
commandPool->free();
|
||||
pipeline.reset();
|
||||
swapChain.reset();
|
||||
}
|
||||
|
||||
void on_resize_callback(GLFWwindow *, int, int) {
|
||||
Renderer::Get()->setResized();
|
||||
}
|
||||
void set_current_extent(VkSurfaceCapabilitiesKHR &capabilities, GLFWwindow* ptr) {
|
||||
if(capabilities.currentExtent.width != INT32_MAX) {
|
||||
return;
|
||||
}
|
||||
auto windowSize = std::make_pair<int, int>(0, 0);
|
||||
glfwGetFramebufferSize(ptr, &windowSize.first, &windowSize.second);
|
||||
capabilities.currentExtent = VkExtent2D{
|
||||
std::max(capabilities.minImageExtent.width, std::min<uint32_t>(capabilities.maxImageExtent.width, windowSize.first)),
|
||||
std::max(capabilities.minImageExtent.height, std::min<uint32_t>(capabilities.maxImageExtent.height, windowSize.second))};
|
||||
};
|
||||
|
||||
bool Renderer::Load(Window& window, const renderOptions& opt) {
|
||||
Window::CreateInfo windowInfo;
|
||||
windowInfo.pfnResize = on_resize_callback;
|
||||
windowInfo.client = {Window::CreateInfo::Client::Type::VK, 0, 0};
|
||||
windowInfo.samples = 1;
|
||||
if (!window.create(windowInfo))
|
||||
return false;
|
||||
|
||||
if (volkInitialize() != VK_SUCCESS) {
|
||||
LOG_E("Failed to initialize Vulkan");
|
||||
return false;
|
||||
}
|
||||
|
||||
VkInstance instance;
|
||||
std::vector<const char *> layers;
|
||||
{
|
||||
VkApplicationInfo appInfo{};
|
||||
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||
appInfo.pApplicationName = "Univerxel";
|
||||
appInfo.applicationVersion = VK_MAKE_VERSION(0, 0, 1);
|
||||
appInfo.pEngineName = "No Engine";
|
||||
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
|
||||
appInfo.apiVersion = VK_API_VERSION_1_2;
|
||||
|
||||
VkInstanceCreateInfo createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
createInfo.pApplicationInfo = &appInfo;
|
||||
|
||||
std::vector<const char *> extensions;
|
||||
{ // Check extensions
|
||||
uint32_t availableExtensionCount = 0;
|
||||
vkEnumerateInstanceExtensionProperties(nullptr, &availableExtensionCount, nullptr);
|
||||
std::vector<VkExtensionProperties> availableExtensions(availableExtensionCount);
|
||||
vkEnumerateInstanceExtensionProperties(nullptr, &availableExtensionCount, availableExtensions.data());
|
||||
|
||||
#if DEBUG
|
||||
LOG_D("Available extensions:");
|
||||
for (const auto &extension : availableExtensions) {
|
||||
LOG_D('\t' << extension.extensionName << " : " << extension.specVersion);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t glfwExtensionCount = 0;
|
||||
const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
|
||||
extensions.reserve(glfwExtensionCount);
|
||||
for (uint32_t i = 0; i < glfwExtensionCount; i++) {
|
||||
if (std::none_of(availableExtensions.begin(), availableExtensions.end(), [&](const VkExtensionProperties &ex) { return strcmp(ex.extensionName, glfwExtensions[i]) == 0; })) {
|
||||
LOG_E("Missing required glfw extension " << glfwExtensions[i]);
|
||||
return false;
|
||||
}
|
||||
extensions.push_back(glfwExtensions[i]);
|
||||
}
|
||||
|
||||
}
|
||||
createInfo.enabledExtensionCount = extensions.size();
|
||||
createInfo.ppEnabledExtensionNames = extensions.data();
|
||||
|
||||
{ // Check layers
|
||||
uint32_t availableLayerCount = 0;
|
||||
vkEnumerateInstanceLayerProperties(&availableLayerCount, nullptr);
|
||||
std::vector<VkLayerProperties> availableLayers(availableLayerCount);
|
||||
vkEnumerateInstanceLayerProperties(&availableLayerCount, availableLayers.data());
|
||||
|
||||
#if DEBUG
|
||||
LOG_D("Available layers:");
|
||||
for (const auto &layer : availableLayers) {
|
||||
LOG_D('\t' << layer.layerName << " : " << layer.specVersion);
|
||||
}
|
||||
#endif
|
||||
|
||||
const auto hasLayer = [&availableLayers](const char *layer) {
|
||||
return std::any_of(availableLayers.begin(), availableLayers.end(), [&layer](const VkLayerProperties &l) { return strcmp(l.layerName, layer) == 0; });
|
||||
};
|
||||
|
||||
if constexpr (VALIDATION_LAYER) {
|
||||
constexpr auto VALIDATION_LAYER_NAME = "VK_LAYER_KHRONOS_validation";
|
||||
if (hasLayer(VALIDATION_LAYER_NAME)) {
|
||||
layers.push_back(VALIDATION_LAYER_NAME);
|
||||
} else {
|
||||
LOG_W("Validation layer unavailable");
|
||||
}
|
||||
if (hasLayer(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) {
|
||||
layers.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
} else {
|
||||
LOG_W("Debug utils layer unavailable");
|
||||
}
|
||||
}
|
||||
}
|
||||
createInfo.enabledLayerCount = layers.size();
|
||||
createInfo.ppEnabledLayerNames = layers.data();
|
||||
|
||||
if (vkCreateInstance(&createInfo, ALLOC, &instance) != VK_SUCCESS) {
|
||||
LOG_E("Failed to create Vulkan instance");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(LOAD_DEVICE) {
|
||||
volkLoadInstanceOnly(instance);
|
||||
} else {
|
||||
volkLoadInstance(instance);
|
||||
}
|
||||
|
||||
const auto version = volkGetInstanceVersion();
|
||||
LOG_D("Vulkan " << VK_VERSION_MAJOR(version) << '.' << VK_VERSION_MINOR(version) << '.' << VK_VERSION_PATCH(version) << ", GLSL TODO:");
|
||||
|
||||
VkSurfaceKHR surface;
|
||||
if (glfwCreateWindowSurface(instance, window.getPtr(), ALLOC, &surface) != VK_SUCCESS) {
|
||||
LOG_E("Failed to create window surface!");
|
||||
return false;
|
||||
}
|
||||
|
||||
PhysicalDeviceInfo physicalInfo;
|
||||
std::vector<const char *> requiredExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
||||
{
|
||||
uint32_t deviceCount = 0;
|
||||
vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
|
||||
if (deviceCount == 0) {
|
||||
LOG_E("Any GPU with Vulkan support");
|
||||
return false;
|
||||
}
|
||||
std::vector<VkPhysicalDevice> devices(deviceCount);
|
||||
vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
|
||||
|
||||
uint bestScore = 0;
|
||||
for(const auto& device: devices) {
|
||||
uint score = 1;
|
||||
VkPhysicalDeviceProperties deviceProperties;
|
||||
VkPhysicalDeviceFeatures deviceFeatures;
|
||||
vkGetPhysicalDeviceProperties(device, &deviceProperties);
|
||||
vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
|
||||
|
||||
if (!deviceFeatures.geometryShader)
|
||||
continue;
|
||||
|
||||
{
|
||||
uint32_t availableExtensionsCount;
|
||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &availableExtensionsCount, nullptr);
|
||||
std::vector<VkExtensionProperties> availableExtensions(availableExtensionsCount);
|
||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &availableExtensionsCount, availableExtensions.data());
|
||||
|
||||
if (std::any_of(requiredExtensions.begin(), requiredExtensions.end(), [&](const char *required) {
|
||||
return std::none_of(availableExtensions.begin(), availableExtensions.end(), [&](const VkExtensionProperties &ex) {
|
||||
return strcmp(ex.extensionName, required) == 0;
|
||||
});
|
||||
}))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
|
||||
score += 10000;
|
||||
|
||||
score += deviceProperties.limits.maxImageDimension2D;
|
||||
//TODO: check others limits
|
||||
|
||||
auto infos = PhysicalDeviceInfo{device, window.getPtr(),
|
||||
SwapChainSupportDetails::Query(device, surface), QueueFamilyIndices::Query(device, surface),
|
||||
VkSurfaceFormatKHR(), surface};
|
||||
|
||||
if (!infos.queueIndices.isComplete())
|
||||
continue;
|
||||
|
||||
if (!infos.swapDetails.isValid())
|
||||
continue;
|
||||
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
physicalInfo = infos;
|
||||
}
|
||||
}
|
||||
if (physicalInfo.device == VK_NULL_HANDLE) {
|
||||
LOG_E("Any GPU matching requirements");
|
||||
return false;
|
||||
}
|
||||
VkPhysicalDeviceProperties deviceProperties;
|
||||
vkGetPhysicalDeviceProperties(physicalInfo.device, &deviceProperties);
|
||||
LOG_D("Using " << deviceProperties.deviceName);
|
||||
}
|
||||
|
||||
VkDevice device;
|
||||
{
|
||||
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
|
||||
{
|
||||
std::set<uint32_t> uniqueQueueFamilies = {physicalInfo.queueIndices.graphicsFamily.value(), physicalInfo.queueIndices.presentFamily.value()};
|
||||
|
||||
const float queuePriority = 1.0f;
|
||||
for (uint32_t queueFamily : uniqueQueueFamilies)
|
||||
{
|
||||
VkDeviceQueueCreateInfo queueCreateInfo{};
|
||||
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||
queueCreateInfo.queueFamilyIndex = queueFamily;
|
||||
queueCreateInfo.queueCount = 1;
|
||||
queueCreateInfo.pQueuePriorities = &queuePriority;
|
||||
queueCreateInfos.push_back(queueCreateInfo);
|
||||
}
|
||||
}
|
||||
|
||||
VkPhysicalDeviceFeatures deviceFeatures{};
|
||||
//TODO:
|
||||
|
||||
VkDeviceCreateInfo createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
createInfo.pQueueCreateInfos = queueCreateInfos.data();
|
||||
createInfo.queueCreateInfoCount = queueCreateInfos.size();
|
||||
createInfo.pEnabledFeatures = &deviceFeatures;
|
||||
createInfo.enabledLayerCount = layers.size();
|
||||
createInfo.ppEnabledLayerNames = layers.data();
|
||||
createInfo.enabledExtensionCount = requiredExtensions.size();
|
||||
createInfo.ppEnabledExtensionNames = requiredExtensions.data();
|
||||
|
||||
if (vkCreateDevice(physicalInfo.device, &createInfo, ALLOC, &device) != VK_SUCCESS) {
|
||||
LOG_E("Failed to bind graphic device");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(LOAD_DEVICE) {
|
||||
volkLoadDevice(device);
|
||||
}
|
||||
|
||||
sInstance = new Renderer(instance, device, physicalInfo, opt);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Renderer::loadUI(Window& w) {
|
||||
UI::Load(w);
|
||||
}
|
||||
|
||||
SwapChainSupportDetails SwapChainSupportDetails::Query(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||
SwapChainSupportDetails swapDetails;
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &swapDetails.capabilities);
|
||||
|
||||
uint32_t formatCount;
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
|
||||
if (formatCount != 0) {
|
||||
swapDetails.formats.resize(formatCount);
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, swapDetails.formats.data());
|
||||
}
|
||||
|
||||
uint32_t presentModeCount;
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
|
||||
if (presentModeCount != 0) {
|
||||
swapDetails.presentModes.resize(presentModeCount);
|
||||
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, swapDetails.presentModes.data());
|
||||
}
|
||||
return swapDetails;
|
||||
}
|
||||
|
||||
QueueFamilyIndices QueueFamilyIndices::Query(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||
QueueFamilyIndices queueIndices;
|
||||
uint32_t queueFamilyCount = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
|
||||
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
|
||||
|
||||
int i = 0;
|
||||
for(const auto& queueFamily: queueFamilies) {
|
||||
if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||||
queueIndices.graphicsFamily = i;
|
||||
}
|
||||
VkBool32 presentSupport = false;
|
||||
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
|
||||
if (presentSupport) {
|
||||
queueIndices.presentFamily = i;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
LOG_D("Queue " << i << ' ' << (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT ? "graphics " : "")
|
||||
<< (queueFamily.queueFlags & VK_QUEUE_COMPUTE_BIT ? "compute " : "")
|
||||
<< (queueFamily.queueFlags & VK_QUEUE_TRANSFER_BIT ? "transfer " : "")
|
||||
<< (queueFamily.queueFlags & VK_QUEUE_PROTECTED_BIT ? "protected " : "")
|
||||
<< 'x' << queueFamily.queueCount);
|
||||
#endif
|
||||
i++;
|
||||
}
|
||||
|
||||
return queueIndices;
|
||||
}
|
||||
|
||||
void Renderer::beginFrame() {
|
||||
assert(currentImage == UINT32_MAX);
|
||||
|
||||
//FIXME: TracyGpuZone("Render");
|
||||
if (auto newImage = swapChain->acquireNextImage(imageAvailableSemaphores[currentFrame], inFlightFences[currentFrame])) {
|
||||
currentImage = newImage.value();
|
||||
} else {
|
||||
recreateSwapChain();
|
||||
beginFrame();
|
||||
}
|
||||
}
|
||||
|
||||
std::function<buffer::params(glm::mat4)> Renderer::beginWorldPass() {
|
||||
assert(currentImage < swapChain->getImageViews().size());
|
||||
|
||||
commandPool->submitGraphics(currentImage, graphicsQueue, imageAvailableSemaphores[currentFrame],
|
||||
renderFinishedSemaphores[currentFrame], inFlightFences[currentFrame]);
|
||||
|
||||
/*WorldPass->useIt();
|
||||
WorldPass->start(this);*/
|
||||
return [&](glm::mat4) {
|
||||
return buffer::params{}; //WorldPass->setup(this, model);
|
||||
};
|
||||
}
|
||||
|
||||
std::function<buffer::params(const std::vector<glm::mat4> &)> Renderer::beginEntityPass() {
|
||||
/*EntityPass->useIt();
|
||||
EntityPass->start(this);*/
|
||||
return [&](const std::vector<glm::mat4>&) {
|
||||
return buffer::params{}; //EntityPass->setup(this, models);
|
||||
};
|
||||
}
|
||||
|
||||
size_t Renderer::drawIndicatorCube(glm::mat4) {
|
||||
/*IndicatorPass->useIt();
|
||||
return IndicatorCubeBuffer.draw(IndicatorPass->setup(this, model));*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Renderer::endPass() {
|
||||
/*if(SkyEnable) {
|
||||
SkyPass->draw(this);
|
||||
}*/
|
||||
}
|
||||
|
||||
void Renderer::swapBuffer(Window&) {
|
||||
/*TracyGpuZone("Swap");
|
||||
glfwSwapBuffers(ptr);
|
||||
TracyGpuCollect;*/
|
||||
|
||||
if(!swapChain->presentImage(currentImage, presentQueue, renderFinishedSemaphores[currentFrame]) || framebufferResized) {
|
||||
framebufferResized = false;
|
||||
recreateSwapChain();
|
||||
}
|
||||
|
||||
currentFrame = (currentFrame + 1) % renderFinishedSemaphores.size();
|
||||
currentImage = UINT32_MAX;
|
||||
|
||||
vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
|
||||
}
|
||||
|
||||
void Renderer::reloadShaders(const passOptions&) {
|
||||
/*WorldPass = std::make_unique<pass::WorldProgram>(options);
|
||||
EntityPass = std::make_unique<pass::EntityProgram>(options);*/
|
||||
}
|
||||
void Renderer::reloadTextures(const std::string&, float, float) {
|
||||
/*unloadTextures();
|
||||
loadTextures(texturePath, mipMapLOD, anisotropy);*/
|
||||
}
|
||||
|
||||
void Renderer::lookFrom(const Camera&) {
|
||||
/*ProjectionMatrix = camera.getProjectionMatrix();
|
||||
ViewMatrix = camera.getViewMatrix();
|
||||
FogDepth = camera.getDepth();*/
|
||||
}
|
||||
|
||||
void Renderer::setClearColor(glm::vec4) {
|
||||
/*FogColor = c;
|
||||
glClearColor(c.r, c.g, c.b, c.a);*/
|
||||
}
|
||||
void Renderer::setCurvature(glm::vec4, float) {
|
||||
/*SphereProj = sp;
|
||||
Curvature = c;*/
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
#include "../Renderer.hpp"
|
||||
#include "forward.hpp"
|
||||
|
||||
namespace render::vk {
|
||||
class SwapChain;
|
||||
class Pipeline;
|
||||
class CommandPool;
|
||||
|
||||
/// Vulkan rendering
|
||||
class Renderer final: public render::Renderer {
|
||||
public:
|
||||
virtual ~Renderer();
|
||||
|
||||
static bool Load(Window& window, const renderOptions& options);
|
||||
|
||||
void loadUI(Window &) override;
|
||||
|
||||
static _FORCE_INLINE_ Renderer *Get() { return static_cast<Renderer*>(render::Renderer::Get()); }
|
||||
|
||||
void beginFrame() override;
|
||||
std::function<buffer::params(glm::mat4)> beginWorldPass() override;
|
||||
std::function<buffer::params(const std::vector<glm::mat4> &)> beginEntityPass() override;
|
||||
size_t drawIndicatorCube(glm::mat4 model) override;
|
||||
void endPass() override;
|
||||
void swapBuffer(Window&) override;
|
||||
|
||||
void setClearColor(glm::vec4) override;
|
||||
void setCurvature(glm::vec4, float) override;
|
||||
|
||||
/// Apply camera matrices
|
||||
void lookFrom(const Camera &) override;
|
||||
void reloadShaders(const passOptions &) override;
|
||||
void reloadTextures(const std::string &, float mipMapLOD, float anisotropy) override;
|
||||
|
||||
void setResized() { framebufferResized = true; }
|
||||
|
||||
private:
|
||||
Renderer(VkInstance, VkDevice, const PhysicalDeviceInfo &, const renderOptions &);
|
||||
|
||||
renderOptions options;
|
||||
|
||||
VkInstance instance;
|
||||
VkSurfaceKHR surface;
|
||||
VkDevice device;
|
||||
VkQueue graphicsQueue;
|
||||
VkQueue presentQueue;
|
||||
|
||||
std::unique_ptr<PhysicalDeviceInfo> physicalInfo;
|
||||
std::unique_ptr<SwapChain> swapChain;
|
||||
std::unique_ptr<Pipeline> pipeline;
|
||||
std::unique_ptr<CommandPool> commandPool;
|
||||
|
||||
size_t currentFrame = 0;
|
||||
uint32_t currentImage = UINT32_MAX;
|
||||
std::vector<VkSemaphore> imageAvailableSemaphores;
|
||||
std::vector<VkSemaphore> renderFinishedSemaphores;
|
||||
std::vector<VkFence> inFlightFences;
|
||||
|
||||
bool framebufferResized = false;
|
||||
void recreateSwapChain();
|
||||
void destroySwapChain();
|
||||
};
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
#include "SwapChain.hpp"
|
||||
|
||||
#include "shared.hpp"
|
||||
|
||||
using namespace render::vk;
|
||||
|
||||
SwapChain::SwapChain(VkDevice device, const PhysicalDeviceInfo& info): device(device) {
|
||||
{ // Swapchain
|
||||
VkPresentModeKHR presentMode = [&]() {
|
||||
// MAYBE: add prefer no triple buffering options
|
||||
for (const auto& availablePresentMode: info.swapDetails.presentModes) {
|
||||
if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
|
||||
return availablePresentMode;
|
||||
}
|
||||
}
|
||||
|
||||
return VK_PRESENT_MODE_FIFO_KHR;
|
||||
}();
|
||||
|
||||
uint32_t imageCount = info.swapDetails.capabilities.minImageCount + 1;
|
||||
if (info.swapDetails.capabilities.maxImageCount > 0 && imageCount > info.swapDetails.capabilities.maxImageCount) {
|
||||
imageCount = info.swapDetails.capabilities.maxImageCount;
|
||||
}
|
||||
|
||||
VkSwapchainCreateInfoKHR createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||
createInfo.surface = info.surface;
|
||||
createInfo.minImageCount = imageCount;
|
||||
createInfo.imageFormat = info.surfaceFormat.format;
|
||||
createInfo.imageColorSpace = info.surfaceFormat.colorSpace;
|
||||
createInfo.imageExtent = info.swapDetails.capabilities.currentExtent;
|
||||
createInfo.imageArrayLayers = 1;
|
||||
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; //VK_IMAGE_USAGE_TRANSFER_DST_BIT
|
||||
|
||||
if (info.queueIndices.graphicsFamily != info.queueIndices.presentFamily) {
|
||||
uint32_t queueFamilyIndices[] = {info.queueIndices.graphicsFamily.value(), info.queueIndices.presentFamily.value()};
|
||||
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||
createInfo.queueFamilyIndexCount = 2;
|
||||
createInfo.pQueueFamilyIndices = queueFamilyIndices;
|
||||
} else {
|
||||
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
createInfo.queueFamilyIndexCount = 0;
|
||||
createInfo.pQueueFamilyIndices = nullptr;
|
||||
}
|
||||
createInfo.preTransform = info.swapDetails.capabilities.currentTransform;
|
||||
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
createInfo.presentMode = presentMode;
|
||||
createInfo.clipped = VK_TRUE;
|
||||
createInfo.oldSwapchain = chain;
|
||||
|
||||
if (vkCreateSwapchainKHR(device, &createInfo, ALLOC, &chain) != VK_SUCCESS) {
|
||||
FATAL("Failed to create swap chain!");
|
||||
}
|
||||
}
|
||||
|
||||
{ // Images
|
||||
uint32_t imageCount;
|
||||
vkGetSwapchainImagesKHR(device, chain, &imageCount, nullptr);
|
||||
images.resize(imageCount);
|
||||
vkGetSwapchainImagesKHR(device, chain, &imageCount, images.data());
|
||||
|
||||
imageViews.resize(images.size());
|
||||
for (size_t i = 0; i < images.size(); i++) {
|
||||
VkImageViewCreateInfo createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
createInfo.image = images[i];
|
||||
|
||||
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||
createInfo.format = info.surfaceFormat.format;
|
||||
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
|
||||
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
createInfo.subresourceRange.baseMipLevel = 0;
|
||||
createInfo.subresourceRange.levelCount = 1;
|
||||
createInfo.subresourceRange.baseArrayLayer = 0;
|
||||
createInfo.subresourceRange.layerCount = 1;
|
||||
|
||||
if (vkCreateImageView(device, &createInfo, ALLOC, &imageViews[i]) != VK_SUCCESS) {
|
||||
FATAL("Failed to create image views!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imagesInFlight.resize(imageViews.size(), VK_NULL_HANDLE);
|
||||
}
|
||||
SwapChain::~SwapChain() {
|
||||
for (auto imageView: imageViews) {
|
||||
vkDestroyImageView(device, imageView, ALLOC);
|
||||
}
|
||||
vkDestroySwapchainKHR(device, chain, ALLOC);
|
||||
}
|
||||
|
||||
std::optional<uint32_t> SwapChain::acquireNextImage(VkSemaphore semaphore, VkFence fence) {
|
||||
uint32_t imageIndex;
|
||||
auto result = vkAcquireNextImageKHR(device, chain, UINT64_MAX, semaphore, VK_NULL_HANDLE, &imageIndex);
|
||||
if (result == VK_ERROR_OUT_OF_DATE_KHR)
|
||||
return {};
|
||||
|
||||
if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
|
||||
FATAL("Failed to acquire swap chain image!");
|
||||
}
|
||||
|
||||
// Check if a previous frame is using this image (i.e. there is its fence to wait on)
|
||||
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
|
||||
vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
|
||||
}
|
||||
// Mark the image as now being in use by this frame
|
||||
imagesInFlight[imageIndex] = fence;
|
||||
return imageIndex;
|
||||
}
|
||||
bool SwapChain::presentImage(uint32_t idx, VkQueue queue, VkSemaphore signalSemaphore) {
|
||||
VkSemaphore signalSemaphores[] = {signalSemaphore};
|
||||
|
||||
VkPresentInfoKHR presentInfo{};
|
||||
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
presentInfo.waitSemaphoreCount = 1;
|
||||
presentInfo.pWaitSemaphores = signalSemaphores;
|
||||
|
||||
VkSwapchainKHR swapChains[] = {chain};
|
||||
presentInfo.swapchainCount = 1;
|
||||
presentInfo.pSwapchains = swapChains;
|
||||
presentInfo.pImageIndices = &idx;
|
||||
presentInfo.pResults = nullptr;
|
||||
|
||||
auto result = vkQueuePresentKHR(queue, &presentInfo);
|
||||
|
||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
||||
return false;
|
||||
} else if (result != VK_SUCCESS) {
|
||||
FATAL("Failed to present swap chain image!");
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include "forward.hpp"
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
namespace render::vk {
|
||||
|
||||
class SwapChain {
|
||||
public:
|
||||
SwapChain(VkDevice, const PhysicalDeviceInfo &);
|
||||
~SwapChain();
|
||||
|
||||
const std::vector<VkImageView> &getImageViews() { return imageViews; }
|
||||
|
||||
std::optional<uint32_t> acquireNextImage(VkSemaphore, VkFence);
|
||||
bool presentImage(uint32_t, VkQueue, VkSemaphore);
|
||||
|
||||
private:
|
||||
VkDevice device;
|
||||
|
||||
VkSwapchainKHR chain;
|
||||
std::vector<VkImage> images;
|
||||
std::vector<VkImageView> imageViews;
|
||||
std::vector<VkFence> imagesInFlight;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#include "UI.hpp"
|
||||
|
||||
#include "../../Window.hpp"
|
||||
#include <GL/gl3w.h>
|
||||
|
||||
using namespace render::vk;
|
||||
|
||||
UI::UI(GLFWwindow *window): render::UI() {
|
||||
// Setup Platform/Renderer bindings
|
||||
/*ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||
ImGui_ImplOpenGL3_Init("#version 130");
|
||||
aim = Texture::CreatePtr("ui/Aim", false);*/
|
||||
}
|
||||
UI::~UI() {
|
||||
/*ImGui_ImplGlfw_Shutdown();
|
||||
ImGui_ImplOpenGL3_Shutdown();*/
|
||||
}
|
||||
|
||||
UI::Actions UI::draw(config::client::options &o, state::state &s, const state::reports &r) {
|
||||
/*ImGui_ImplOpenGL3_NewFrame();
|
||||
ImGui_ImplGlfw_NewFrame();*/
|
||||
return UI::Actions::None; //render::UI::draw(o, s, r, aim);
|
||||
}
|
||||
void UI::render() {
|
||||
//ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||
}
|
||||
|
||||
void UI::Load(Window& w) {
|
||||
UI::sInstance = new UI(w.getPtr());
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "../UI.hpp"
|
||||
struct GLFWwindow;
|
||||
class Window;
|
||||
|
||||
namespace render::vk {
|
||||
|
||||
/// ImGui Vulkan/GLFW interface
|
||||
class UI final: public render::UI {
|
||||
private:
|
||||
UI(GLFWwindow *window);
|
||||
public:
|
||||
~UI();
|
||||
|
||||
static void Load(Window &);
|
||||
|
||||
Actions draw(config::client::options &, state::state &, const state::reports &) override;
|
||||
void render() override;
|
||||
|
||||
private:
|
||||
intptr_t aim;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <volk.h>
|
||||
#include <tuple>
|
||||
namespace render { struct renderOptions; }
|
||||
namespace render::vk {
|
||||
struct PhysicalDeviceInfo;
|
||||
using PipelineRef = std::pair<VkRenderPass, VkPipeline>;
|
||||
constexpr VkAllocationCallbacks *ALLOC = nullptr;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "forward.hpp"
|
||||
#include "../../../core/utils/logger.hpp"
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
struct GLFWwindow;
|
||||
namespace render::vk {
|
||||
struct SwapChainSupportDetails {
|
||||
VkSurfaceCapabilitiesKHR capabilities;
|
||||
std::vector<VkSurfaceFormatKHR> formats;
|
||||
std::vector<VkPresentModeKHR> presentModes;
|
||||
static SwapChainSupportDetails Query(VkPhysicalDevice, VkSurfaceKHR);
|
||||
bool isValid() const { return !formats.empty() && !presentModes.empty(); }
|
||||
};
|
||||
struct QueueFamilyIndices {
|
||||
std::optional<uint32_t> graphicsFamily;
|
||||
std::optional<uint32_t> presentFamily;
|
||||
static QueueFamilyIndices Query(VkPhysicalDevice, VkSurfaceKHR);
|
||||
bool isComplete() const { return graphicsFamily.has_value() && presentFamily.has_value(); }
|
||||
};
|
||||
struct PhysicalDeviceInfo {
|
||||
VkPhysicalDevice device = VK_NULL_HANDLE;
|
||||
GLFWwindow *window;
|
||||
SwapChainSupportDetails swapDetails;
|
||||
QueueFamilyIndices queueIndices;
|
||||
VkSurfaceFormatKHR surfaceFormat;
|
||||
VkSurfaceKHR surface;
|
||||
};
|
||||
}
|
|
@ -11,6 +11,7 @@ namespace world::client {
|
|||
return std::make_unique<DistantUniverse>(ct.value(), distOpts);
|
||||
#ifndef STANDALONE
|
||||
} else if(localHandle != nullptr) {
|
||||
LOG_D("Using local universe");
|
||||
return std::make_unique<LocalUniverse>(localHandle);
|
||||
#endif
|
||||
} else {
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <fstream>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include "../server/config.hpp"
|
||||
#include "../client/config.hpp"
|
||||
|
||||
namespace config {
|
||||
|
||||
|
@ -21,8 +23,7 @@ public:
|
|||
LOG_E("Config file " << path << " not found. Creating default");
|
||||
return toml::table();
|
||||
}();
|
||||
if (config["server"]["enabled"].value_or(false))
|
||||
{
|
||||
if (config["server"]["enabled"].value_or(false)) {
|
||||
_server = server::options(config["server"]);
|
||||
}
|
||||
if(config["client"]["enabled"].value_or(false)) {
|
||||
|
|
|
@ -15,7 +15,7 @@ public:
|
|||
if(auto addr = ct.toAddress())
|
||||
return addr.value();
|
||||
|
||||
FATAL("Invalid ip server address format");
|
||||
FATAL("Invalid server address format");
|
||||
}();
|
||||
|
||||
host = enet_host_create(NULL, 1, CHANNEL_COUNT, 0, 0);
|
||||
|
|
Loading…
Reference in New Issue