Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e81c940
Extract sphere and cylinder mesh generation into shared graphics utility
laanwj Feb 7, 2026
a7bb295
Route ImGui calls through gr_screen function pointers
laanwj Feb 8, 2026
529200a
Free bitmaps before destroying graphics backend
laanwj Feb 8, 2026
1ff78bc
Use float shader input instead of SCREEN_POS in gr_flash_internal
laanwj Feb 10, 2026
de380e9
Remove now-unused SCREEN_POS vertex format
laanwj Feb 10, 2026
bc3b321
Add dds_block_size and dds_compressed_mip_size utilities
laanwj Feb 15, 2026
3975a89
Add CAPABILITY_QUERIES_REUSABLE for GPU queries
laanwj Feb 16, 2026
0186e87
Fix gr_flip debug output ordering
laanwj Feb 16, 2026
7fb789f
Fix gr_end_2d_matrix viewport for render-to-texture
laanwj Feb 16, 2026
7f7c958
Fix undefined gl_ClipDistance and use uint for std140 bool
laanwj Feb 16, 2026
a92d908
Fix shader build MAIN_DEPENDENCY and add conditional GLSL/struct gene…
laanwj Feb 16, 2026
01db4df
Add missing memcpy_if_trivial_else_error for `void *, const void*`
laanwj Feb 16, 2026
8c531c3
Add Vulkan rendering backend
laanwj Feb 16, 2026
61f890e
Remove Vulkan SDK build dependency by bundling Vulkan headers
laanwj Mar 1, 2026
b1a2558
Fix Vulkan transfer queue selection logic
laanwj Mar 2, 2026
a0b7d28
Fix cubemap render target switching with proper render pass management
laanwj Mar 2, 2026
d70eae6
Fix irrmap resume bug: correctly return to swap chain pass instead of…
laanwj Mar 2, 2026
f96e431
Fix G-buffer corruption from undefined MODEL shader outputs in Vulkan
laanwj Mar 2, 2026
02d83e3
Fix shadow map fallback using wrong image view type in deferred lighting
laanwj Mar 2, 2026
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
12 changes: 2 additions & 10 deletions code/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -113,16 +113,8 @@ if (FSO_BUILD_WITH_OPENGL)
target_compile_definitions(code PUBLIC WITH_OPENGL)
endif()
if (FSO_BUILD_WITH_VULKAN)
find_package(Vulkan REQUIRED)
if (Vulkan_FOUND)
target_compile_definitions(code PUBLIC WITH_VULKAN)
target_link_libraries(code PRIVATE Vulkan::Vulkan)

target_compile_definitions(code PUBLIC WITH_VULKAN VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1 VK_NO_PROTOTYPES)
else()
message(WARNING "FSO_BUILD_WITH_VULKAN was set, but the package was unable to be found. Forcing OFF.")
set(FSO_BUILD_WITH_VULKAN OFF CACHE BOOL "Enable compilation of the Vulkan renderer" FORCE)
endif()
target_compile_definitions(code PUBLIC WITH_VULKAN VULKAN_HPP_DISPATCH_LOADER_DYNAMIC=1 VK_NO_PROTOTYPES)
target_link_libraries(code PUBLIC VulkanHeaders)
endif()

include(shaders.cmake)
Expand Down
2 changes: 1 addition & 1 deletion code/ddsutils/ddsutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ static size_t compute_dds_size(const DDS_HEADER &dds_header, bool converting = f

if (dds_header.ddspf.dwFlags & DDPF_FOURCC) {
// size of data block (4x4)
d_size += ((d_width + 3) / 4) * ((d_height + 3) / 4) * d_depth * ((dds_header.ddspf.dwFourCC == FOURCC_DXT1) ? 8 : 16);
d_size += dds_compressed_mip_size(d_width, d_height, (dds_header.ddspf.dwFourCC == FOURCC_DXT1) ? 8 : 16) * d_depth;
} else {
d_size += d_width * d_height * d_depth * (dds_header.ddspf.dwRGBBitCount / 8);
}
Expand Down
21 changes: 21 additions & 0 deletions code/ddsutils/ddsutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,27 @@ typedef struct {
} DDS_HEADER_DXT10;
#pragma pack()

// Block size in bytes for a 4x4 texel block of a compressed DDS format.
// comp_type is one of the DDS_DXT*/DDS_CUBEMAP_DXT* constants.
inline int dds_block_size(int comp_type) {
switch (comp_type) {
case DDS_DXT1:
case DDS_CUBEMAP_DXT1:
return 8;
case DDS_DXT3: case DDS_CUBEMAP_DXT3:
case DDS_DXT5: case DDS_CUBEMAP_DXT5:
case DDS_BC7:
return 16;
default:
return 0;
}
}

// Size in bytes of one mip level of a block-compressed texture.
inline size_t dds_compressed_mip_size(int w, int h, int block_size) {
return static_cast<size_t>(((w + 3) / 4) * ((h + 3) / 4) * block_size);
}

#define DDS_OFFSET 4+sizeof(DDS_HEADER) //place where the data starts -- should be 128
#define DX10_OFFSET DDS_OFFSET+sizeof(DDS_HEADER_DXT10) // Unless a DX10 header is present

Expand Down
5 changes: 5 additions & 0 deletions code/globalincs/pstypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,11 @@ inline void* memset_if_trivial_else_error(ImDrawListSplitter* memset_data, int c
return ptr_memcpy(memcpy_dest, memcpy_src, count);
}

inline void *memcpy_if_trivial_else_error(void *memcpy_dest, const void *memcpy_src, size_t count)
{
return ptr_memcpy(memcpy_dest, memcpy_src, count);
}

// MEMMOVE!
const auto ptr_memmove = std::memmove;
#define memmove memmove_if_trivial_else_error
Expand Down
22 changes: 15 additions & 7 deletions code/graphics/2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,10 @@ void gr_close()

graphics::paths::PathRenderer::shutdown();

// Free bitmaps before destroying the graphics backend, since
// gf_bm_free_data needs the backend (texture manager, GL context, etc.)
bm_close();

switch (gr_screen.mode) {
case GR_OPENGL:
#ifdef WITH_OPENGL
Expand All @@ -1324,13 +1328,11 @@ void gr_close()

case GR_STUB:
break;

default:
Int3(); // Invalid graphics mode
}

bm_close();

Gr_inited = 0;
}

Expand Down Expand Up @@ -2924,6 +2926,16 @@ void gr_flip(bool execute_scripting)
}
}

if (Cmdline_graphics_debug_output) {
output_uniform_debug_data();
}

// IMPORTANT: No rendering may happen after this point until gf_flip()/gr_setup_frame().
// gr_reset_immediate_buffer() resets the write offset to 0, so any subsequent immediate
// buffer write would overwrite vertex data that already-recorded draw commands reference.
// In Vulkan (deferred submission), the GPU reads the final buffer state at submit time,
// so overwrites here silently corrupt earlier draws. OpenGL's immediate execution hides
// this, but it is still logically wrong for any deferred-submission backend.
gr_reset_immediate_buffer();

// Do per frame operations on the matrix state
Expand All @@ -2933,10 +2945,6 @@ void gr_flip(bool execute_scripting)

mouse_reset_deltas();

if (Cmdline_graphics_debug_output) {
output_uniform_debug_data();
}

// Use this opportunity for retiring the uniform buffers
uniform_buffer_managers_retire_buffers();

Expand Down
13 changes: 11 additions & 2 deletions code/graphics/2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ enum shader_type {

SDR_TYPE_IRRADIANCE_MAP_GEN,

SDR_TYPE_SHADOW_MAP,

NUM_SHADER_TYPES
};

Expand Down Expand Up @@ -262,7 +264,6 @@ struct vertex_format_data
POSITION4,
POSITION3,
POSITION2,
SCREEN_POS,
COLOR3,
COLOR4,
COLOR4F,
Expand Down Expand Up @@ -339,7 +340,8 @@ enum class gr_capability {
CAPABILITY_PERSISTENT_BUFFER_MAPPING,
CAPABILITY_BPTC,
CAPABILITY_LARGE_SHADER,
CAPABILITY_INSTANCED_RENDERING
CAPABILITY_INSTANCED_RENDERING,
CAPABILITY_QUERIES_REUSABLE
};

struct gr_capability_def {
Expand Down Expand Up @@ -934,6 +936,10 @@ typedef struct screen {

std::function<void(bool set_override)> gf_override_fog;

// ImGui backend integration
std::function<void()> gf_imgui_new_frame;
std::function<void()> gf_imgui_render_draw_data;

//OpenXR functions
std::function<SCP_vector<const char*>()> gf_openxr_get_extensions;
std::function<bool()> gf_openxr_test_capabilities;
Expand Down Expand Up @@ -1189,6 +1195,9 @@ inline void gr_post_process_restore_zbuffer()

#define gr_override_fog GR_CALL(gr_screen.gf_override_fog)

#define gr_imgui_new_frame GR_CALL(gr_screen.gf_imgui_new_frame)
#define gr_imgui_render_draw_data GR_CALL(gr_screen.gf_imgui_render_draw_data)

inline void gr_render_primitives(material* material_info,
primitive_type prim_type,
vertex_layout* layout,
Expand Down
32 changes: 27 additions & 5 deletions code/graphics/matrix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,18 @@ static void create_perspective_projection_matrix(matrix4 *out, float left, float
out->a1d[5] = 2.0f * near_dist / (top - bottom);
out->a1d[8] = (right + left) / (right - left);
out->a1d[9] = (top + bottom) / (top - bottom);
out->a1d[10] = -(far_dist + near_dist) / (far_dist - near_dist);
out->a1d[11] = -1.0f;
out->a1d[14] = -2.0f * far_dist * near_dist / (far_dist - near_dist);

if (gr_screen.mode == GR_VULKAN) {
// Vulkan NDC Z range is [0, 1] (OpenGL is [-1, 1])
// Y-flip is handled by negative viewport height (VK_KHR_maintenance1)
out->a1d[10] = -far_dist / (far_dist - near_dist);
out->a1d[14] = -far_dist * near_dist / (far_dist - near_dist);
} else {
// OpenGL NDC Z range is [-1, 1]
out->a1d[10] = -(far_dist + near_dist) / (far_dist - near_dist);
out->a1d[14] = -2.0f * far_dist * near_dist / (far_dist - near_dist);
}
}

static void create_orthographic_projection_matrix(matrix4* out, float left, float right, float bottom, float top, float near_dist, float far_dist)
Expand All @@ -64,11 +73,20 @@ static void create_orthographic_projection_matrix(matrix4* out, float left, floa

out->a1d[0] = 2.0f / (right - left);
out->a1d[5] = 2.0f / (top - bottom);
out->a1d[10] = -2.0f / (far_dist - near_dist);
out->a1d[12] = -(right + left) / (right - left);
out->a1d[13] = -(top + bottom) / (top - bottom);
out->a1d[14] = -(far_dist + near_dist) / (far_dist - near_dist);
out->a1d[15] = 1.0f;

if (gr_screen.mode == GR_VULKAN) {
// Vulkan NDC Z range is [0, 1] (OpenGL is [-1, 1])
// Y-flip is handled by negative viewport height (VK_KHR_maintenance1)
out->a1d[10] = -1.0f / (far_dist - near_dist);
out->a1d[14] = -near_dist / (far_dist - near_dist);
} else {
// OpenGL NDC Z range is [-1, 1]
out->a1d[10] = -2.0f / (far_dist - near_dist);
out->a1d[14] = -(far_dist + near_dist) / (far_dist - near_dist);
}
}

void gr_start_instance_matrix(const vec3d *offset, const matrix *rotation)
Expand Down Expand Up @@ -272,7 +290,11 @@ void gr_end_2d_matrix()
Assert( htl_2d_matrix_depth == 1 );

// reset viewport to what it was originally set to by the proj matrix
gr_set_viewport(gr_screen.offset_x, (gr_screen.max_h - gr_screen.offset_y - gr_screen.clip_height), gr_screen.clip_width, gr_screen.clip_height);
if (gr_screen.rendering_to_texture != -1) {
gr_set_viewport(gr_screen.offset_x, gr_screen.offset_y, gr_screen.clip_width, gr_screen.clip_height);
} else {
gr_set_viewport(gr_screen.offset_x, (gr_screen.max_h - gr_screen.offset_y - gr_screen.clip_height), gr_screen.clip_width, gr_screen.clip_height);
}

gr_projection_matrix = gr_last_projection_matrix;

Expand Down
17 changes: 17 additions & 0 deletions code/graphics/opengl/gropengl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
#include "osapi/osregistry.h"
#include "pngutils/pngutils.h"

#include "backends/imgui_impl_opengl3.h"

#include <glad/glad.h>

// minimum GL version we can reliably support is 3.2
Expand Down Expand Up @@ -972,6 +974,16 @@ int opengl_init_display_device()
return 0;
}

static void gr_opengl_imgui_new_frame()
{
ImGui_ImplOpenGL3_NewFrame();
}

static void gr_opengl_imgui_render_draw_data()
{
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}

void gr_opengl_init_function_pointers()
{
gr_screen.gf_flip = gr_opengl_flip;
Expand Down Expand Up @@ -1104,6 +1116,9 @@ void gr_opengl_init_function_pointers()

gr_screen.gf_override_fog = gr_opengl_override_fog;

gr_screen.gf_imgui_new_frame = gr_opengl_imgui_new_frame;
gr_screen.gf_imgui_render_draw_data = gr_opengl_imgui_render_draw_data;

gr_screen.gf_openxr_get_extensions = gr_opengl_openxr_get_extensions;
gr_screen.gf_openxr_test_capabilities = gr_opengl_openxr_test_capabilities;
gr_screen.gf_openxr_create_session = gr_opengl_openxr_create_session;
Expand Down Expand Up @@ -1502,6 +1517,8 @@ bool gr_opengl_is_capable(gr_capability capability)
return !Cmdline_no_large_shaders;
case gr_capability::CAPABILITY_INSTANCED_RENDERING:
return GLAD_GL_ARB_vertex_attrib_binding;
case gr_capability::CAPABILITY_QUERIES_REUSABLE:
return true;
}


Expand Down
Loading
Loading