Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 37 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends \
ninja-build pkg-config \
ninja-build pkg-config binutils \
xorg-dev libx11-dev libwayland-dev libxkbcommon-dev libglu1-mesa-dev \
libvulkan-dev

Expand Down Expand Up @@ -208,6 +208,42 @@ jobs:
if: runner.os == 'Windows'
run: ctest --test-dir build -C Debug --output-on-failure --no-tests=error --timeout 300

# Build with the Vulkan SDK, then run Sandbox where libvulkan.so.1 cannot be
# loaded. The windowed run must exit 2 and log the missing-runtime line.
# --null-render must still exit 0, because it does not open the loader.
# The mount namespace hides the host copies. It does not uninstall packages
# and it does not change the other jobs.
no-vulkan-runtime:
name: Linux no Vulkan runtime
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
with:
submodules: recursive

- name: Install Linux dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends \
ninja-build pkg-config \
xorg-dev libx11-dev libwayland-dev libxkbcommon-dev libglu1-mesa-dev \
libvulkan-dev

- name: Build and install Catch2
run: |
cmake -DFLING_INIT_CONFIGS=Release \
-DFLING_INIT_SKIP_SUBMODULES=ON -DFLING_INIT_SKIP_CONFIGURE=ON \
-P scripts/init.cmake

- name: Configure
run: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release

- name: Build Sandbox
run: cmake --build build --target Sandbox --parallel

- name: Run with the loader hidden
run: bash FlingTests/VulkanLoad/RunWithoutVulkanLoader.sh build/Sandbox/bin/Sandbox

# Doc-comment conventions for first-party sources (see Skills/coding-style.md / issue #166).
# clang-format cannot enforce comment *content*; this script does.
comment-style:
Expand Down
11 changes: 10 additions & 1 deletion Assets/Shaders/Deferred/deferred.frag
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,16 @@ layout (binding = 7) uniform UBO
void main()
{
// Get G-Buffer values
vec3 fragPos = texture(samplerposition, inUV).rgb;
vec4 position = texture(samplerposition, inUV);

// The offscreen pass writes alpha 1 for geometry and clears to 0. Keep what is
// already in the frame (the skybox, if there is one) where there is no geometry.
if (position.a == 0.0)
{
discard;
}

vec3 fragPos = position.rgb;
vec3 normal = normalize(texture(samplerNormal, inUV).rgb);
vec4 albedo = texture(samplerAlbedo, inUV);
float roughness = texture(samplerRoughness, inUV).x;
Expand Down
Binary file modified Assets/Shaders/Deferred/deferred_frag.spv
Binary file not shown.
18 changes: 14 additions & 4 deletions Assets/Shaders/skybox/skybox.frag
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
#version 450

// Inverse of projection * view, with the camera translation removed. @see SkyboxSubpass.cpp
layout (binding = 0) uniform UBO
{
mat4 invViewProjection;
} ubo;

layout (binding = 1) uniform samplerCube samplerCubeMap;

layout (location = 0) in vec3 inUVW;
layout (location = 0) in vec2 inNDC;

layout (location = 0) out vec4 outFragColor;

void main()
void main()
{
outFragColor = texture(samplerCubeMap, inUVW);
}
// Change the pixel back to a world space direction from the camera
vec4 farPoint = ubo.invViewProjection * vec4(inNDC, 1.0, 1.0);
vec3 direction = normalize(farPoint.xyz / farPoint.w);

outFragColor = vec4(texture(samplerCubeMap, direction).rgb, 1.0);
}
Binary file modified Assets/Shaders/skybox/skybox.frag.spv
Binary file not shown.
25 changes: 9 additions & 16 deletions Assets/Shaders/skybox/skybox.vert
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
#version 450

layout (location = 0) in vec3 inPos;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexCoord;
// Normalized device coordinates of this pixel, for the fragment shader to make a view ray
layout (location = 0) out vec2 outNDC;

layout (binding = 0) uniform UBO
{
mat4 projection;
mat4 modelView;
} ubo;

layout (location = 0) out vec3 outUVW;

out gl_PerVertex
out gl_PerVertex
{
vec4 gl_Position;
};

void main()
void main()
{
vec3 position = mat3(ubo.modelView) * inPos.xyz;
gl_Position = (ubo.projection * vec4(position, 0.0)).xyzz;
outUVW = inPos.xyz;
// One triangle that covers the full screen, made from the vertex index with no vertex buffer
// @see https://www.saschawillems.de/blog/2016/08/13/vulkan-tutorial-on-rendering-a-fullscreen-quad-without-buffers/
vec2 uv = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
outNDC = uv * 2.0 - 1.0;
gl_Position = vec4(outNDC, 1.0, 1.0);
}
Binary file modified Assets/Shaders/skybox/skybox.vert.spv
Binary file not shown.
5 changes: 5 additions & 0 deletions Config/EngineConf.ini
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ WindowIconImage=FlingEngineLogo.png
EnableValidationLayers=false
#EnableValidationLayers=true

[Graphics]
; Cube map folder for the skybox, relative to the Assets directory. Leave empty for no skybox.
; @see Cubemap.h for the face file names
Skybox=Textures/Skybox/Garden

[Camera]
MoveSpeed=10
RotationSpeed=700
Expand Down
2 changes: 1 addition & 1 deletion FlingEngine/Application/src/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ namespace Fling
else
{
VulkanApp::Get().Init(
static_cast<PipelineFlags>(PipelineFlags::DEFERRED | PipelineFlags::IMGUI),
static_cast<PipelineFlags>(PipelineFlags::DEFERRED | PipelineFlags::CUBEMAP | PipelineFlags::IMGUI),
g_Registry,
m_DebugUI
);
Expand Down
8 changes: 8 additions & 0 deletions FlingEngine/Core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ fling_add_module(Core
FlingExt::json # Input recording files are JSON Lines, hidden in InputRecording.cpp
)

# DynamicLibrary is the only dlopen caller. Older glibc keeps it in libdl.
target_link_libraries(Core PRIVATE ${CMAKE_DL_LIBS})

# DynamicLibrary calls WinVerifyTrust before LoadLibraryExW.
if(WIN32)
target_link_libraries(Core PRIVATE wintrust)
endif()

# Feature flags are read from Core headers (FlingTypes.h) and from every module
# that links Core, so they live here and propagate to everything.
target_compile_definitions(Core PUBLIC
Expand Down
91 changes: 91 additions & 0 deletions FlingEngine/Core/inc/DynamicLibrary.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#pragma once

#include "CoreAPI.h"

#include <span>
#include <string>
#include <string_view>

namespace Fling
{
/**
* Load and unload one shared library.
*
* This is the only Fling type that calls LoadLibrary or dlopen.
* Move a DynamicLibrary. Do not copy it.
*
* Open loads a library with the platform search path.
* It does not check a signature. Use it for game modules and plugins.
* OpenSigned is the strict open. On Windows it checks Authenticode
* before the load. Linux has no Authenticode check, so OpenSigned
* calls Open there.
*/
class CORE_API DynamicLibrary
{
public:
DynamicLibrary() = default;

/** Unload the library when this object is still open. */
~DynamicLibrary();

DynamicLibrary(const DynamicLibrary&) = delete;
DynamicLibrary& operator=(const DynamicLibrary&) = delete;

DynamicLibrary(DynamicLibrary&& t_Other) noexcept;
DynamicLibrary& operator=(DynamicLibrary&& t_Other) noexcept;

/**
* Open the shared library t_Name.
*
* An empty object means that the load failed. When t_Error is not
* null, write the platform message there on failure. Clear t_Error
* on success.
*/
static DynamicLibrary Open(std::string_view t_Name, std::string* t_Error = nullptr);

/**
* Open t_Name only when Windows trusts its Authenticode signature.
*
* A valid signature is embedded or catalog. A bare name is resolved
* in System32, then in the executable directory. The current
* directory and PATH are not searched. An explicit path contains a
* slash, a backslash, or a drive prefix. That path is verified and
* loaded alone. A missing file sets "The shared library was not found".
* On Linux this calls Open.
*/
static DynamicLibrary OpenSigned(std::string_view t_Name, std::string* t_Error = nullptr);

/**
* Open the first library in t_Names that loads.
*
* When every name fails and t_Error is not null, write the last
* platform message.
*/
static DynamicLibrary OpenFirst(std::span<const std::string_view> t_Names, std::string* t_Error = nullptr);

/** True when a library is open. */
explicit operator bool() const;

/**
* Return the address of t_Name.
* Return null when the library is closed or the symbol is missing.
*/
void* Symbol(std::string_view t_Name) const;

/**
* Return the address of t_Name as T_Fn.
* Return null when the library is closed or the symbol is missing.
*/
template <typename T_Fn>
T_Fn SymbolAs(std::string_view t_Name) const
{
return reinterpret_cast<T_Fn>(Symbol(t_Name));
}

/** Close the library. A second call does nothing. */
void Close();

private:
void* m_Handle = nullptr;
};
} // namespace Fling
Loading