Compare commits

..
Author SHA1 Message Date
SirKnightly 2b57379fed Automated build commit 2026-11-03 01:27:32 +00:00
228 changed files with 1026 additions and 19699 deletions

No files matched your search

+4 -13
View File
@@ -57,15 +57,6 @@ endif()
PROJECT(FS2_Open LANGUAGES ${FSO_LANGUAGES})
if (ANDROID)
message(STATUS "Building for Android (NDK)")
set(FSO_BUILD_INCLUDED_LIBS ON CACHE BOOL "" FORCE)
set(USING_PREBUILT_LIBS ON CACHE BOOL "" FORCE)
set(SDL3_USE_PRECOMPILED ON CACHE BOOL "" FORCE)
set(OPENAL_USE_PRECOMPILED ON CACHE BOOL "" FORCE)
set(USING_PREBUILT_VULKAN ON CACHE BOOL "" FORCE)
endif()
# Check if the external modules exists
IF(NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib/libRocket/Build")
message(FATAL_ERROR "External submodules could not be found. Please make sure you have updated your submodules.")
@@ -118,8 +109,8 @@ OPTION(FSO_BUILD_TESTS "Build unit tests" OFF)
OPTION(FSO_DEVELOPMENT_MODE "Generate binaries in development mode, only use if you know what you're doing!" OFF)
IF(WIN32 OR APPLE OR ANDROID)
# On windows, mac and android the default should be to always build the included libraries
IF(WIN32 OR APPLE)
# On windows and mac the default should be to always build the included libraries
SET(FSO_BUILD_INCLUDED_LIBS_DEFAULT ON)
ELSE()
SET(FSO_BUILD_INCLUDED_LIBS_DEFAULT OFF)
@@ -147,13 +138,13 @@ OPTION(FSO_BUILD_WITH_DISCORD "Build with Discord support" ON)
OPTION(FSO_BUILD_WITH_OPENGL "Enable compilation of the OpenGL renderer" ON)
OPTION(FSO_BUILD_WITH_OPENGL_DEBUG "Enables debug option for OpenGL" OFF)
OPTION(FSO_BUILD_WITH_OPENGL_ES "When building OpenGL, use OpenGL ES compatibility layer" OFF)
OPTION(FSO_BUILD_WITH_OPENGL_ES "When building OpenGL, use OpenGL ES compatibility layer (External shaders only!)" OFF)
OPTION(FSO_BUILD_WITH_VULKAN "Enable compilation of the Vulkan renderer" ON)
if(NOT FSO_BUILD_WITH_OPENGL AND FSO_BUILD_WITH_OPENGL_ES)
# Disable GLES if not building with OpenGL
set(FSO_BUILD_WITH_OPENGL_ES OFF CACHE BOOL "" FORCE)
set(FSO_BUILD_WITH_OPENGL_ES OFF FORCE)
endif()
+1 -1
View File
@@ -7,7 +7,7 @@ if (EXISTS "${PROJECT_SOURCE_DIR}/version_override.cmake")
endif()
set_if_not_defined(FSO_VERSION_MAJOR 26)
set_if_not_defined(FSO_VERSION_MINOR 3)
set_if_not_defined(FSO_VERSION_MINOR 1)
set_if_not_defined(FSO_VERSION_BUILD 0)
# A numeric revision e.g. the CI build number
-1
View File
@@ -77,7 +77,6 @@ TARGET_LINK_LIBRARIES(code PUBLIC jansson)
target_link_libraries(code PUBLIC anl)
target_link_libraries(code PUBLIC imgui)
target_link_libraries(code PUBLIC implot)
IF(FSO_BUILD_WITH_OPENXR)
target_link_libraries(code PUBLIC OpenXR::openxr_loader)
+1 -1
View File
@@ -1693,7 +1693,7 @@ static void asteroid_do_area_effect(object *asteroid_objp)
if ( weapon_area_calc_damage(ship_objp, &asteroid_objp->pos, asip->inner_rad, asip->outer_rad, asip->blast, asip->damage, &blast, &damage, asip->outer_rad) == -1 )
continue;
ship_apply_global_damage(ship_objp, asteroid_objp, &asteroid_objp->pos, damage, asip->damage_type_idx, DamageSource::Explosion);
ship_apply_global_damage(ship_objp, asteroid_objp, &asteroid_objp->pos, damage, asip->damage_type_idx);
weapon_area_apply_blast(nullptr, ship_objp, &asteroid_objp->pos, blast, false);
} // end for
}
-7
View File
@@ -1036,13 +1036,6 @@ void bm_init() {
bm_inited = true;
}
bool bm_is_cubemap(int handle)
{
const BM_TYPE type = bm_get_entry(handle)->comp_type;
return type == BM_TYPE_CUBEMAP_DDS || type == BM_TYPE_CUBEMAP_DXT1 || type == BM_TYPE_CUBEMAP_DXT3 ||
type == BM_TYPE_CUBEMAP_DXT5;
}
int bm_is_compressed(int num) {
auto entry = bm_get_entry(num);
BM_TYPE type = BM_TYPE_NONE;
-8
View File
@@ -594,14 +594,6 @@ void bm_get_components(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a);
*/
int bm_is_compressed(int handle);
/**
* @brief Returns true if the bitmap indexed by handle was loaded from a cubemap image file
*
* @note Render targets return false, even cubemap ones, and so do BC7 (DX10) DDS cubemaps, whose cube flag the DDS
* loader does not keep
*/
bool bm_is_cubemap(int handle);
/**
* @brief Gets the correct TCACHE_TYPE for compressed graphics (uncompressed are assumed TCACHE_TYPE_NORMAL)
*/
+15 -90
View File
@@ -33,7 +33,7 @@
#include "parse/encrypt.h"
#include "cfilesystem.h"
#include <algorithm>
#include <limits>
char Cfile_root_dir[CFILE_ROOT_DIRECTORY_LEN] = "";
@@ -185,12 +185,7 @@ int cfile_init(const char *exe_dir, const char *cdrom_dir)
char buf[CFILE_ROOT_DIRECTORY_LEN];
#ifndef __ANDROID__
strncpy(buf, exe_dir, CFILE_ROOT_DIRECTORY_LEN - 1);
#else
(void)exe_dir;
strncpy(buf, os_android_get_working_folder_path().c_str(), CFILE_ROOT_DIRECTORY_LEN - 1);
#endif
strncpy(buf, exe_dir, CFILE_ROOT_DIRECTORY_LEN - 1);
buf[CFILE_ROOT_DIRECTORY_LEN - 1] = '\0';
@@ -1110,98 +1105,28 @@ void cfread_string(char *buf, int n, CFILE *file)
} while (c != 0 );
}
namespace {
struct string_len_header {
int len = 0;
int stored = 0;
int start = 0;
bool length_cut = false;
};
// a past-the-end length is clamped like any cfread, but a negative one has nothing to clamp to, so inside a
// limited section it throws and the loader skips the rest of the section
string_len_header read_string_len_header(CFILE* file)
void cfread_string_len(char *buf, int buffer_size, CFILE *file)
{
string_len_header h;
if (!cf_is_valid(file)) {
return h;
int len;
len = cfread_int(file);
Assertion( (len <= buffer_size), "String cannot fit in buffer, len: %i, buffer: %i", len, buffer_size);
if (len) {
cfread(buf, len, 1, file);
Assertion(len != buffer_size || buf[len - 1] == 0, "Unterminated string does not fit in buffer");
}
int before = cftell(file);
h.length_cut = cfread(&h.len, sizeof(h.len), 1, file) != 1;
h.start = cftell(file);
if (h.length_cut) {
// a clean end of file reads as an empty string without a warning, as cfread_int does
h.length_cut = (h.start != before) || (cfeof(file) == 0);
return h;
}
h.len = INTEL_INT(h.len);
if ((h.len < 0) && (file->max_read_len != 0)) {
throw cfile::max_read_length("Attempted to read a string of negative length " + std::to_string(h.len));
}
h.stored = static_cast<int>(cf_check_read_size(file, static_cast<size_t>(std::max(h.len, 0))));
return h;
}
// reads count of the stored characters and leaves the file just past the whole string; false if the read failed
bool read_string_len_body(const string_len_header& h, char* dest, int count, CFILE* file)
{
bool ok = (count <= 0) || (cfread(dest, count, 1, file) == 1);
if (!ok || (h.stored > count)) {
cfseek(file, h.start + h.stored, CF_SEEK_SET);
}
return ok;
}
// warned last, so the file position is right even if a debug build stops here
void warn_string_len(const string_len_header& h, bool read_ok, bool cut, int buffer_size, CFILE* file)
{
if (h.length_cut) {
Warning(LOCATION, "cfread_string_len: cut-off string length field in %s", cf_get_filename(file));
} else if (!read_ok) {
Warning(LOCATION,
"cfread_string_len: could not read a string of %d characters in %s",
h.stored,
cf_get_filename(file));
} else if (h.len != h.stored) {
Warning(LOCATION, "cfread_string_len: corrupt string length %d in %s", h.len, cf_get_filename(file));
} else if (cut) {
Warning(LOCATION,
"cfread_string_len: a string of %d bytes was cut to fit a buffer of %d in %s",
h.len,
buffer_size,
cf_get_filename(file));
}
}
} // namespace
void cfread_string_len(char* buf, int buffer_size, CFILE* file)
{
Assertion(buffer_size > 0, "cfread_string_len needs room for at least the terminator, buffer: %i", buffer_size);
string_len_header h = read_string_len_header(file);
if (buffer_size <= 0) {
bool read_ok = read_string_len_body(h, buf, 0, file);
warn_string_len(h, read_ok, h.stored > 0, buffer_size, file);
return;
}
int count = std::min(h.stored, buffer_size);
bool read_ok = read_string_len_body(h, buf, count, file);
int kept = read_ok ? count : 0;
bool cut = (kept == buffer_size) && (memchr(buf, 0, buffer_size) == nullptr);
buf[std::min(kept, buffer_size - 1)] = 0;
warn_string_len(h, read_ok, cut, buffer_size, file);
buf[MIN(len, buffer_size - 1)] = 0;
}
SCP_string cfread_string_len(CFILE *file)
{
string_len_header h = read_string_len_header(file);
int len = cfread_int(file);
SCP_string str;
str.resize(h.stored);
bool read_ok = read_string_len_body(h, str.data(), h.stored, file);
if (!read_ok) {
str.clear();
}
warn_string_len(h, read_ok, false, 0, file);
str.resize(len);
if (len)
cfread(&str[0], len, 1, file);
return str;
}
+2 -8
View File
@@ -320,26 +320,20 @@ void cfread_string(char *buf,int n, CFILE *file);
/**
* @brief Read a fixed length string that is not null-terminated, with the length stored in file.
*
* Keeps at most buffer_size - 1 characters and leaves the file just past the whole string, unless it throws.
*
* If it is not null-terminated, the length of the string *must* be less than the size of the buffer (to make room for one)
*
* @param buf Pre-allocated array to store string
* @param buffer_size Size of pre-allocated array
* @param file File to read from
*
* @note Appends NULL character to string (buf)
* @note Bad data gets a warning; at a clean end of file, or with an invalid file, the string is empty and nothing is
* warned. Inside a limited section (cf_set_max_read_len), a negative length, or a read that still passes the limit
* once clamped to the end of the file, throws cfile::max_read_length.
*/
void cfread_string_len(char *buf,int buffer_size, CFILE *file);
/**
* @brief Read a string from the file where the length is stored in the file
*
* @param file The file to read the string from
* @return The string that was read
*
* @note Warns and throws like the char* overload, except that nothing is cut.
*/
SCP_string cfread_string_len(CFILE *file);
+14 -24
View File
@@ -193,8 +193,21 @@ int cfseek( CFILE *cfile, int offset, int where )
}
size_t cf_check_read_size(CFILE* cfile, size_t size)
// cfread() reads from a file
//
// returns: returns the number of full elements read
//
//
int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
{
if(!cf_is_valid(cfile))
return 0;
size_t size = elsize*nelem;
if(buf == NULL || size <= 0)
return 0;
if ( (cfile->raw_position+size) > cfile->size ) {
Assertion(cfile->raw_position <= cfile->size, "Invalid raw_position value detected!");
size = cfile->size - cfile->raw_position;
@@ -213,29 +226,6 @@ size_t cf_check_read_size(CFILE* cfile, size_t size)
}
}
return size;
}
// cfread() reads from a file
//
// returns: returns the number of full elements read
//
//
int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
{
if(!cf_is_valid(cfile))
return 0;
size_t size = elsize*nelem;
if(buf == nullptr || size <= 0)
return 0;
size = cf_check_read_size(cfile, size);
if (size < 1) {
return 0;
}
size_t bytes_read;
if (cfile->data != nullptr) {
// This is a file from memory
-5
View File
@@ -62,9 +62,4 @@ void cf_check_compression(CFILE* cfile);
// Used to clear compression info data and free dynamic memory used by compression
void cf_clear_compression_info(CFILE* cfile);
// Clamps a read of size bytes to the end of the file and returns the clamped size, or 0 at the end of the file
// without checking the limit; throws cfile::max_read_length if the clamped read would pass the file's read limit
// (cf_set_max_read_len).
size_t cf_check_read_size(CFILE* cfile, size_t size);
#endif
+54 -88
View File
@@ -75,7 +75,6 @@ const char *cmdline_arg_types[] =
enum class flag_output_type {
Binary,
Json_V1,
Json_V2,
};
// variables
@@ -556,12 +555,7 @@ int Cmdline_show_pos = 0;
int Cmdline_show_stats = 0;
int Cmdline_save_render_targets = 0;
bool Cmdline_window = false;
#ifdef __ANDROID__
// Default to borderless in the legacy window-mode path as well.
bool Cmdline_fullscreen_window = true;
#else
bool Cmdline_fullscreen_window = false;
#endif
std::optional<std::pair<uint16_t, uint16_t>>Cmdline_window_res = std::nullopt;
char *Cmdline_res = 0;
char *Cmdline_center_res = 0;
@@ -1364,58 +1358,51 @@ static void handle_unix_modlist(char **modlist, size_t *len)
// external entry point into this modules
static void json_add_version(json_t* root) {
auto version_obj = json_object();
json_object_set_new(version_obj, "full", json_string(FS_VERSION_FULL));
json_object_set_new(version_obj, "major", json_integer(FS_VERSION_MAJOR));
json_object_set_new(version_obj, "minor", json_integer(FS_VERSION_MINOR));
json_object_set_new(version_obj, "build", json_integer(FS_VERSION_BUILD));
json_object_set_new(version_obj, "has_revision", json_boolean(FS_VERSION_HAS_REVIS));
json_object_set_new(version_obj, "revision", json_integer(FS_VERSION_REVIS));
json_object_set_new(version_obj, "revision_str", json_string(FS_VERSION_REVIS_STR));
json_object_set_new(root, "version", version_obj);
}
static void json_add_easy_flags(json_t* root) {
auto easy_array = json_array();
for (auto& easy_flag : easy_flags) {
json_array_append_new(easy_array, json_string(easy_flag.name));
}
json_object_set_new(root, "easy_flags", easy_array);
}
static void json_add_flags(json_t* root) {
auto flags_array = json_array();
for (auto& flag : exe_params) {
auto flag_obj = json_object();
json_object_set_new(flag_obj, "name", json_string(flag.name));
json_object_set_new(flag_obj, "description", json_string(flag.desc));
json_object_set_new(flag_obj, "fso_only", json_boolean(flag.fso_only));
json_object_set_new(flag_obj, "on_flags", json_integer(flag.on_flags));
json_object_set_new(flag_obj, "off_flags", json_integer(flag.off_flags));
json_object_set_new(flag_obj, "type", json_string(flag.type));
json_object_set_new(flag_obj, "web_url", json_string(flag.web_url));
json_array_append_new(flags_array, flag_obj);
}
json_object_set_new(root, "flags", flags_array);
}
static json_t* json_get_v1() {
auto root = json_object();
json_add_version(root);
json_add_easy_flags(root);
json_add_flags(root);
{
auto version_obj = json_object();
json_object_set_new(version_obj, "full", json_string(FS_VERSION_FULL));
json_object_set_new(version_obj, "major", json_integer(FS_VERSION_MAJOR));
json_object_set_new(version_obj, "minor", json_integer(FS_VERSION_MINOR));
json_object_set_new(version_obj, "build", json_integer(FS_VERSION_BUILD));
json_object_set_new(version_obj, "has_revision", json_boolean(FS_VERSION_HAS_REVIS));
json_object_set_new(version_obj, "revision", json_integer(FS_VERSION_REVIS));
json_object_set_new(version_obj, "revision_str", json_string(FS_VERSION_REVIS_STR));
json_object_set_new(root, "version", version_obj);
}
{
auto easy_array = json_array();
for (auto& easy_flag : easy_flags) {
json_array_append_new(easy_array, json_string(easy_flag.name));
}
json_object_set_new(root, "easy_flags", easy_array);
}
{
auto flags_array = json_array();
for (auto& flag : exe_params) {
auto flag_obj = json_object();
json_object_set_new(flag_obj, "name", json_string(flag.name));
json_object_set_new(flag_obj, "description", json_string(flag.desc));
json_object_set_new(flag_obj, "fso_only", json_boolean(flag.fso_only));
json_object_set_new(flag_obj, "on_flags", json_integer(flag.on_flags));
json_object_set_new(flag_obj, "off_flags", json_integer(flag.off_flags));
json_object_set_new(flag_obj, "type", json_string(flag.type));
json_object_set_new(flag_obj, "web_url", json_string(flag.web_url));
json_array_append_new(flags_array, flag_obj);
}
json_object_set_new(root, "flags", flags_array);
}
{
auto caps_array = json_array();
@@ -1526,19 +1513,6 @@ static json_t* json_get_v1() {
return root;
}
// json_v2 reports version, supported flags, and the preferences path for launchers,
// without querying graphics, audio, or joystick devices.
static json_t* json_get_v2() {
auto root = json_object();
json_add_version(root);
json_add_easy_flags(root);
json_add_flags(root);
json_object_set_new(root, "pref_path", json_string(os_get_config_path().c_str()));
return root;
}
static void write_flags_file() {
FILE *fp = fopen("flags.lch","w");
@@ -1596,8 +1570,6 @@ static flag_output_type get_flags_output_type() {
return flag_output_type::Binary;
} else if (type == "json_v1") {
return flag_output_type::Json_V1;
} else if (type == "json_v2") {
return flag_output_type::Json_V2;
} else {
// This is supposed to make it easy for the launcher to recognize an unsupported type
printf("OUTPUT TYPE NOT SUPPORTED!\n");
@@ -1608,23 +1580,17 @@ static flag_output_type get_flags_output_type() {
static void write_flags() {
auto type = get_flags_output_type();
if (type == flag_output_type::Binary) {
switch(type) {
case flag_output_type::Binary:
write_flags_file();
return;
break;
case flag_output_type::Json_V1:
json_t* root = json_get_v1();
json_dumpf(root, stdout, JSON_INDENT(4));
json_decref(root);
break;
}
json_t* root = (type == flag_output_type::Json_V1) ? json_get_v1() : json_get_v2();
#ifdef __ANDROID__
char* dumped = json_dumps(root, JSON_INDENT(4));
os_android_set_flags_string(dumped ? dumped : "");
free(dumped);
#else
json_dumpf(root, stdout, JSON_INDENT(4));
#endif
json_decref(root);
}
bool SetCmdlineParams()
@@ -1821,7 +1787,6 @@ bool SetCmdlineParams()
options::OptionsManager::instance()->setOverride("Graphics.WindowMode",
std::unique_ptr<json_t>(json_integer(static_cast<int>(os::ViewportState::Windowed))), "-window");
Cmdline_window = true;
Cmdline_fullscreen_window = false;
}
if ( fullscreen_window_arg.found( ) )
@@ -2444,9 +2409,10 @@ bool SetCmdlineParams()
if (gr_sync_validation_arg.found()) {
// Enables the Vulkan validation layer + debug messenger + synchronization
// validation feature (see VulkanRenderer::initializeInstance), but NOT the
// engine-level graphics debug output: -gr_debug additionally feeds extra
// stat groups into the ImGui frame profiler overlay (see gr_get_debug_stats),
// which are unrelated to GPU sync validation. Keep this a pure GPU-validation switch.
// engine-level graphics debug output: -gr_debug additionally draws debug
// overlays (e.g. output_uniform_debug_data), which are unrelated to GPU
// sync validation and would otherwise appear as spurious on-screen
// artifacts. Keep this a pure GPU-validation switch.
Cmdline_gr_sync_validation = true;
}
+15 -11
View File
@@ -1322,7 +1322,7 @@ bool control_config_accept(bool API_Access)
// Check if a hardcoded preset with name already exists. If so, complain to user and force retry
auto it = std::find_if(Control_config_presets.begin(), Control_config_presets.end(), [str](CC_preset& p) {
return ((p.name == str) && ((p.get_type() == Preset_t::tbl) || (p.get_type() == Preset_t::hardcode)));
return ((p.name == str) && ((p.type == Preset_t::tbl) || (p.type == Preset_t::hardcode)));
});
if (it != Control_config_presets.end()) {
@@ -1353,14 +1353,15 @@ bool control_config_accept(bool API_Access)
}
// Pack the current bindings into a preset, then save the file
CC_preset preset(std::move(str), Preset_t::pst);
CC_preset preset;
preset.name = std::move(str);
std::copy(Control_config.begin(), Control_config.end(), std::back_inserter(preset.bindings));
Control_config_presets.push_back(preset);
save_preset_file(preset, true);
// Reload the presets from file. Do this instead of just pushing the preset to the vector direct to get
// consistant ordering
Control_config_presets.erase(Control_config_presets.begin() + 1, Control_config_presets.end());
Control_config_presets.resize(1);
load_preset_files();
// finally, save the new preset so that changes will get saved to this preset
@@ -1777,7 +1778,7 @@ bool control_config_create_new_preset(const SCP_string& newName, bool overwrite)
// Check if a hardcoded preset with name already exists. If so, complain to user and force retry
auto it = std::find_if(Control_config_presets.begin(), Control_config_presets.end(), [newName](CC_preset& p) {
return (p.name == newName) && ((p.get_type() == Preset_t::tbl) || (p.get_type() == Preset_t::hardcode));
return (p.name == newName) && ((p.type == Preset_t::tbl) || (p.type == Preset_t::hardcode));
});
if (it != Control_config_presets.end()) {
@@ -1790,7 +1791,9 @@ bool control_config_create_new_preset(const SCP_string& newName, bool overwrite)
}
// Pack the current bindings into a preset, then save the file
CC_preset preset(newName, Preset_t::pst);
CC_preset preset;
preset.name = newName;
preset.type = Preset_t::pst;
std::copy(Control_config.begin(), Control_config.end(), std::back_inserter(preset.bindings));
// Done with the file
@@ -1802,7 +1805,7 @@ bool control_config_create_new_preset(const SCP_string& newName, bool overwrite)
save_preset_file(preset, true);
// Reload the presets from file.
Control_config_presets.erase(Control_config_presets.begin() + 1, Control_config_presets.end());
Control_config_presets.resize(1);
load_preset_files(newName);
// use the newly created preset
@@ -1810,9 +1813,10 @@ bool control_config_create_new_preset(const SCP_string& newName, bool overwrite)
return true;
} else if ((clone->name != preset.name) || (clone->get_type() != Preset_t::pst)) {
} else if ((clone->name != preset.name) || (clone->type != Preset_t::pst)) {
// Complain and ignore if the preset names or the type differs
return false;
}
return false; //should be unreachable, but just in case
@@ -1822,7 +1826,7 @@ bool control_config_clone_preset(const CC_preset& preset, const SCP_string& newN
// Check if a hardcoded preset with name already exists. If so, complain to user and force retry
auto it = std::find_if(Control_config_presets.begin(), Control_config_presets.end(), [newName](CC_preset& p) {
return (p.name == newName) && ((p.get_type() == Preset_t::tbl) || (p.get_type() == Preset_t::hardcode));
return (p.name == newName) && ((p.type == Preset_t::tbl) || (p.type == Preset_t::hardcode));
});
if (it != Control_config_presets.end()) {
@@ -1834,13 +1838,13 @@ bool control_config_clone_preset(const CC_preset& preset, const SCP_string& newN
return false;
}
CC_preset newPreset(newName, Preset_t::pst);
newPreset.bindings = preset.bindings;
CC_preset newPreset = preset;
newPreset.name = newName;
bool success = save_preset_file(newPreset, false);
// Reload the presets from file.
Control_config_presets.erase(Control_config_presets.begin() + 1, Control_config_presets.end());
Control_config_presets.resize(1);
load_preset_files(newName);
// use the newly cloned preset
+10 -18
View File
@@ -572,28 +572,20 @@ public:
* A preset, a collection of bindings for use in Control_config with an associated name
*/
class CC_preset {
public:
public:
SCP_vector<CCB> bindings;
SCP_string name;
Preset_t type = Preset_t::hardcode;
private:
// No default value: the type is always set explicitly by the constructor
Preset_t type;
public:
CC_preset() = default;
CC_preset(const CC_preset& A) = default;
public:
// No default constructor: every preset must be created with an explicit type
CC_preset(SCP_string preset_name, Preset_t preset_type) : name(std::move(preset_name)), type(preset_type) {}
CC_preset(const CC_preset&) = default;
// Preserve target's preset type during copy assignment
CC_preset& operator=(const CC_preset& other);
Preset_t get_type() const
{
return type;
}
bool is_duplicate_of(const CC_preset& other) const;
CC_preset& operator=(const CC_preset&);
/*!
* Checks if the given preset is a duplicate of this one
*/
bool is_duplicate_of(CC_preset&);
};
/*!
+19 -15
View File
@@ -292,8 +292,10 @@ void control_config_common_init_bindings() {
// init default preset
Control_config_presets.clear();
CC_preset preset("default", Preset_t::hardcode);
CC_preset preset;
preset.bindings.reserve(Control_config.size());
preset.name = "default";
preset.type = Preset_t::hardcode;
for (auto &item : Control_config) {
preset.bindings.push_back(CCB(item));
@@ -1731,13 +1733,14 @@ size_t read_bind_1(CC_preset &preset) {
*/
void control_config_common_read_section(int s, bool first_override) {
Assertion((s == 0) || (s == 1), "Expected value of s to be either 0 or 1, found %i instead.", s);
CC_preset new_preset("", Preset_t::tbl);
CC_preset new_preset;
// Set references to the default preset and bindings
auto& default_preset = Control_config_presets[0];
auto& default_bindings = default_preset.bindings;
new_preset.bindings.clear();
new_preset.type = Preset_t::tbl;
if (s == 0) {
// #ControlConfigOverride
@@ -2999,22 +3002,23 @@ CCI_builder& CCI_builder::operator()(IoActionId action_id, short primary, short
return *this;
}
CC_preset& CC_preset::operator=(const CC_preset& other)
{
if (this != &other) {
bindings = other.bindings;
name = other.name;
// 'type' is intentionally preserved to maintain instance identity
}
return *this;
}
CC_preset& CC_preset::operator=(const CC_preset& A) {
name = A.name;
type = A.type;
bool CC_preset::is_duplicate_of(const CC_preset& other) const
{
for (size_t i = 0; i < other.bindings.size(); ++i) {
if (bindings[i] != other.bindings[i]) {
std::copy(A.bindings.begin(), A.bindings.end(), bindings.begin());
return *this;
};
bool CC_preset::is_duplicate_of(CC_preset& A) {
for (size_t i = 0; i < A.bindings.size(); ++i) {
if (bindings[i] != A.bindings[i]) {
// Found a binding that's different. Thus, this preset is not a duplicate
return false;
}
}
// Else, did not find any differences in the bindings. Thus, this preset is a duplicate
return true;
}
+5 -3
View File
@@ -74,7 +74,9 @@ void load_preset_files(SCP_string clone) {
// Start reading in data
CC_preset preset(file, Preset_t::pst);
CC_preset preset;
preset.name = file;
preset.type = Preset_t::pst;
preset.bindings.resize(Control_config.size());
size_t size;
@@ -132,7 +134,7 @@ void load_preset_files(SCP_string clone) {
if ((clone == preset.name) || (it == Control_config_presets.end())) {
Control_config_presets.push_back(preset);
} else if ((it->name != preset.name) || ((it->get_type() != Preset_t::pst))) {
} else if ((it->name != preset.name) || (it->type != Preset_t::pst)) {
if (gameseq_get_state() == GS_STATE_CONTROL_CONFIG) {
popup(PF_TITLE_WHITE | PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, "Preset '%s' is a duplicate of an existing preset, ignoring", preset.name.c_str());
} else {
@@ -171,7 +173,7 @@ bool delete_preset_file(CC_preset preset) {
cf_delete(filename.c_str(), CF_TYPE_PLAYER_BINDS, CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
// Reload the presets from file.
Control_config_presets.erase(Control_config_presets.begin() + 1, Control_config_presets.end());
Control_config_presets.resize(1);
load_preset_files();
return true;
+4 -6
View File
@@ -1,16 +1,12 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2DArray;
#endif
#include "gamma.sdr"
layout(location=0) out vec4 fragOut0;
#ifdef VULKAN
layout (location = 0) in vec4 fragTexCoord;
layout (location = 1) in vec4 fragColor;
layout (location = 0) out vec4 fragOut0;
layout (set = 1, binding = 1) uniform sampler2DArray baseMap;
layout (set = 2, binding = 0, std140) uniform genericData {
@@ -21,6 +17,8 @@ layout (set = 2, binding = 0, std140) uniform genericData {
in vec4 fragTexCoord;
in vec4 fragColor;
out vec4 fragOut0;
uniform sampler2DArray baseMap;
layout (std140) uniform genericData {
@@ -1,7 +1,3 @@
#ifdef GL_ES
precision highp float;
#endif
#ifdef VULKAN
layout (location = 0) in vec4 vertPosition;
layout (location = 1) in vec4 vertColor;
+2 -7
View File
@@ -1,16 +1,11 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2D;
#endif
layout(location=0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D bloomed;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D bloomed;
#endif
+2 -8
View File
@@ -1,15 +1,9 @@
#ifdef GL_ES
precision highp float;
precision highp int;
precision highp sampler2D;
#endif
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
#else
out vec4 fragOut0;
in vec4 fragTexCoord;
uniform sampler2D tex;
#endif
+2 -8
View File
@@ -1,18 +1,12 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2D;
#endif
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
#endif
const float Luminance = 0.08;
const float fMiddleGray = 0.2;
const float fWhiteCutoff = 0.4;
+2 -8
View File
@@ -1,16 +1,10 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2D;
precision highp sampler2DArray;
#endif
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
#ifdef COPY_ARRAY
uniform sampler2DArray tex;
#else
+4 -15
View File
@@ -1,9 +1,3 @@
#ifdef GL_ES
precision highp float;
precision highp int;
precision highp sampler2D;
precision highp sampler2DArray;
#endif
// NOTE: The technique and some of this code is based on this tutorial:
// http://martindevans.me/game-development/2015/02/27/Drawing-Stuff-On-Other-Stuff-With-Deferred-Screenspace-Decals/
@@ -35,14 +29,9 @@ layout (set = 1, binding = 1) uniform sampler2DArray decalTextures;
layout (set = 1, binding = 4) uniform sampler2D gDepthBuffer;
layout (set = 1, binding = 6) uniform sampler2D gNormalBuffer;
#else
layout (location=0) out vec4 fragOut0; // Diffuse buffer
#ifndef GL_ES
layout (location=1) out vec4 fragOut1; // Normal buffer
layout (location=2) out vec4 fragOut2; // Emissive buffer
#else
layout (location=2) out vec4 fragOut1; // Normal buffer -> COLOR_ATTACHMENT2
layout (location=4) out vec4 fragOut2; // Emissive buffer -> COLOR_ATTACHMENT4
#endif
out vec4 fragOut0; // Diffuse buffer
out vec4 fragOut1; // Normal buffer
out vec4 fragOut2; // Emissive buffer
flat in mat4 invModelMatrix;
flat in vec3 decalDirection;
@@ -135,7 +124,7 @@ vec3 getPixelNormal(vec3 frag_position, vec2 tex_coord, inout float alpha, out v
}
// Make a smooth alpha transition leading up to an edge
alpha = alpha * (1.0 - smoothstep(angle_fade_start, normal_angle_cutoff, angle));
alpha = alpha * (1 - smoothstep(angle_fade_start, normal_angle_cutoff, angle));
return normal;
}
-4
View File
@@ -1,7 +1,3 @@
#ifdef GL_ES
precision highp float;
precision highp int;
#endif
#ifdef VULKAN
// Binding 0: box vertex positions
@@ -1,10 +1,3 @@
#ifdef GL_ES
#extension GL_EXT_clip_cull_distance : enable
precision highp float;
precision highp int;
precision highp sampler2DArray;
#endif
#ifdef VULKAN
layout(set = 2, binding = 0, std140)
#else
@@ -27,28 +20,22 @@ uniform genericData {
uint clipEnabled;
};
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(set = 1, binding = 1) uniform sampler2DArray baseMap;
layout(location = 0) in vec4 fragTexCoord;
layout(location = 1) in vec4 fragColor;
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
layout(location = 2) in float vClipDist;
#endif
layout(location = 0) out vec4 fragOut0;
#else
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
in float vClipDist;
#endif
uniform sampler2DArray baseMap;
in vec4 fragTexCoord;
in vec4 fragColor;
out vec4 fragOut0;
#endif
@@ -56,11 +43,6 @@ in vec4 fragColor;
void main()
{
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
if (clipEnabled != 0u && vClipDist < 0.0) {
discard;
}
#endif
vec4 baseColor = texture(baseMap, vec3(fragTexCoord.xy, float(baseMapIndex)));
if(alphaThreshold > baseColor.a) discard;
baseColor.rgb = (srgb == 1) ? srgb_to_linear(baseColor.rgb) : baseColor.rgb;
@@ -1,9 +1,3 @@
#ifdef GL_ES
#extension GL_EXT_clip_cull_distance : enable
precision highp float;
precision highp int;
#endif
#ifdef VULKAN
layout(set = 2, binding = 0, std140)
#else
@@ -44,15 +38,10 @@ layout(location = 2) in vec4 vertTexCoord;
layout(location = 0) out vec4 fragTexCoord;
layout(location = 1) out vec4 fragColor;
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
layout(location = 2) out float vClipDist;
#endif
#else
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
out float vClipDist;
#endif
out float gl_ClipDistance[1];
out vec4 fragTexCoord;
in vec4 vertTexCoord;
@@ -69,16 +58,13 @@ void main()
gl_Position = projMatrix * modelViewMatrix * vertPosition;
if (clipEnabled != 0u) {
#ifndef SDR_FLAG_SW_CLIP_FALLBACK
gl_ClipDistance[0] = dot(clipEquation, modelMatrix * vertPosition);
#else
vClipDist = dot(clipEquation, modelMatrix * vertPosition);
#endif
} else {
#ifndef SDR_FLAG_SW_CLIP_FALLBACK
#ifdef VULKAN
// Vulkan has no glEnable(GL_CLIP_DISTANCE0) equivalent — clip distances are
// always evaluated when declared in the shader. Must write a positive value
// when clipping is disabled to prevent undefined-value vertex culling.
gl_ClipDistance[0] = 1.0;
#else
vClipDist = 1.0;
#endif
#endif
}
}
@@ -1,14 +1,9 @@
#ifdef GL_ES
precision highp float;
#endif
layout (location=0) out vec4 fragOut0;
layout (location=1) out vec4 fragOut1;
layout (location=2) out vec4 fragOut2;
layout (location=3) out vec4 fragOut3;
layout (location=4) out vec4 fragOut4;
layout (location=5) out vec4 fragOut5;
out vec4 fragOut0;
out vec4 fragOut1;
out vec4 fragOut2;
out vec4 fragOut3;
out vec4 fragOut4;
out vec4 fragOut5;
void main()
{
fragOut0 = vec4(0.0, 0.0, 0.0, 1.0); // color
+9 -17
View File
@@ -1,21 +1,13 @@
//? #version 150
#ifdef GL_ES
precision highp float;
precision highp int;
precision highp sampler2D;
precision highp samplerCube;
precision highp sampler2DArrayShadow;
#endif
#include "lighting.sdr" //! #include "lighting.sdr"
#include "gamma.sdr" //! #include "gamma.sdr"
#include "shadows.sdr" //! #include "shadows.sdr"
#include "z-compress.sdr" //! #include "z-compress.sdr"
#ifndef GL_ES
#ifdef VULKAN
layout(location = 0) out vec4 fragOut0;
#else
layout(location = 5) out vec4 fragOut0; //COLOR_ATTACHMENTn is on location n in ES, here it uses COLOR_ATTACHMENT5
out vec4 fragOut0;
#endif
#ifdef VULKAN
@@ -153,7 +145,7 @@ void GetLightInfo(vec3 position, in float alpha, in vec3 reflectDir, out vec3 li
dist = length(lightDirOut);
lightDist = dist;
// Energy conservation term
float alpha_adjust = clamp(alpha + (sourceRadius/(2.0*dist)), 0.0, 1.0);
float alpha_adjust = clamp(alpha + (sourceRadius/(2*dist)), 0.0, 1.0);
area_normalisation = alpha/alpha_adjust;
area_normalisation *= area_normalisation;
//end chunk
@@ -196,7 +188,7 @@ void GetLightInfo(vec3 position, in float alpha, in vec3 reflectDir, out vec3 li
float dist = length(lightDirOut);
lightDist = dist;
// Energy conservation term
float alpha_adjust = min(alpha + (sourceRadius/(2.0*dist)), 1.0);
float alpha_adjust = min(alpha + (sourceRadius/(2*dist)), 1.0);
area_normalisation = alpha/alpha_adjust;
// don't need to square as it's a line rather than a sphere.
//end chunk
@@ -243,11 +235,11 @@ void ComputeEnvLight(float alpha, float ao, vec3 light_dir, vec3 eyeDir, vec3 no
// so let s = (2/alpha^2 - 2)
// 1/2 log(s + 1) = 1/2 log(2/alpha^2 -1)
const float ENV_REZ = 512.0; // Ideally this would be #define'd and shader recompiled with envmap rez changes
const float REZ_BIAS = log2(ENV_REZ * sqrt(3.0));
const float ENV_REZ = 512; // Ideally this would be #define'd and shader recompiled with envmap rez changes
const float REZ_BIAS = log2(ENV_REZ * sqrt(3));
float alphaSqr = alpha * alpha;
float rough_bias = 0.5 * log2(2.0/alphaSqr - 1.0);
float rough_bias = 0.5 * log2(2/alphaSqr - 1);
float mip_bias = REZ_BIAS - rough_bias;
// Sample light, using mip bias to blur it.
@@ -265,7 +257,7 @@ void ComputeEnvLight(float alpha, float ao, vec3 light_dir, vec3 eyeDir, vec3 no
// Pseudo-IBL, so use k_IBL
float k = alpha * alpha/ 2.0f;
float NdotL = max(dot(light_dir, normal),0.0);
float NdotL = max(dot(light_dir, normal),0);
float g1vNL = GeometrySchlickGGX(NdotL, k);
vec3 specEnvLighting = specEnvColour.rgb * fresnel * g1vNL;
@@ -346,6 +338,6 @@ void main()
float NdotL = clamp(dot(normal, lightDir), 0.0, 1.0);
fragmentColor.rgb = computeLighting(specColor.rgb, diffColor, lightDir, normal.xyz, halfVec, eyeDir, roughness, fresnel, NdotL).rgb * diffuseLightColor * attenuation * area_normalisation;
}
fragOut0 = max(fragmentColor, vec4(0.0));
}
@@ -1,7 +1,3 @@
#ifdef GL_ES
precision highp float;
precision highp int;
#endif
#include "lighting.sdr"
@@ -1,16 +1,10 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2D;
precision highp sampler2DArray;
#endif
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec4 fragTexCoord;
layout(location = 1) in vec4 fragColor;
layout(location = 2) in float fragOffset;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2DArray baseMap;
layout(set = 1, binding = 4) uniform sampler2D depthMap;
layout(set = 1, binding = 5) uniform sampler2D frameBuffer;
@@ -20,6 +14,8 @@ in vec4 fragTexCoord;
in vec4 fragColor;
in float fragOffset;
out vec4 fragOut0;
uniform sampler2DArray baseMap;
uniform sampler2D depthMap;
uniform sampler2D distMap;
@@ -1,7 +1,3 @@
#ifdef GL_ES
precision highp float;
#endif
#ifdef VULKAN
layout(location = 0) in vec4 vertPosition;
layout(location = 1) in vec4 vertColor;
+4 -8
View File
@@ -1,21 +1,15 @@
#ifdef GL_ES
precision highp float;
precision highp int;
precision highp sampler2D;
precision highp sampler2DArray;
#endif
#include "gamma.sdr"
#include "z-compress.sdr"
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec4 fragTexCoord;
layout(location = 1) in vec4 fragColor;
layout(location = 2) in float fragRadius;
layout(location = 3) in vec4 fragPosition;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2DArray baseMap;
layout(set = 1, binding = 4) uniform sampler2D depthMap;
#else
@@ -24,6 +18,8 @@ in vec4 fragPosition;
in vec4 fragTexCoord;
in vec4 fragColor;
out vec4 fragOut0;
uniform sampler2DArray baseMap;
uniform sampler2D depthMap;
#endif
-4
View File
@@ -1,7 +1,3 @@
#ifdef GL_ES
precision highp float;
#endif
layout (points) in;
layout (triangle_strip, max_vertices = 4) out;
-3
View File
@@ -1,6 +1,3 @@
#ifdef GL_ES
precision highp float;
#endif
#ifdef VULKAN
layout(location = 0) in vec4 vertPosition;
@@ -1,14 +1,11 @@
#define M_PI 3.1415926535897932384626433832795
#ifdef GL_ES
precision highp float;
precision highp samplerCube;
#endif
uniform samplerCube envmap;
in vec4 fragTexCoord;
layout(location=0) out vec4 fragOut0;
out vec4 fragOut0;
layout (location = 0) out vec4 outColor;
void main() {
const float theta = -fragTexCoord.x * (2.0 * M_PI);
+5 -8
View File
@@ -1,20 +1,17 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2D;
#endif
#include "gamma.sdr"
layout(location=0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
layout(set = 1, binding = 4) uniform sampler2D depth_tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
uniform sampler2D depth_tex;
#endif
@@ -43,7 +40,7 @@ void main()
// If the scene is in fog then we change the color we operate on based on the depth of the pixel
float depth_val = texture(depth_tex, fragTexCoord.xy).x;
// Transform depth value from [0, 1] to [-1, 1]
float depth_normalized = 2.0 * depth_val - 1.0;
float depth_normalized = 2 * depth_val - 1;
// Now we compute the depth value in projection space using the clipping plane information
float view_depth = 2.0 * zNear * zFar / (zFar + zNear - depth_normalized * (zFar - zNear));
@@ -53,7 +50,7 @@ void main()
if (isinf(view_depth)) {
fragOut0.rgb = color_in.rgb;
} else {
float fog_dist = clamp(1.0 - pow(fog_density, view_depth - fog_start) , 0.0, 1.0);
float fog_dist = clamp(1 - pow(fog_density, view_depth - fog_start) , 0.0, 1.0);
vec3 finalFogColor = srgb_to_linear(fog_color);
fragOut0.rgb = mix(color_in.rgb, finalFogColor, fog_dist);
+1 -14
View File
@@ -1,12 +1,3 @@
#ifdef GL_ES
#ifdef GL_EXT_gpu_shader5
#extension GL_EXT_gpu_shader5 : enable
#define FXAA_GATHER4_ALPHA 1
#endif
precision highp float;
precision highp sampler2D;
#endif
#define FXAA_EARLY_EXIT 1
#define FXAA_DISCARD 1
#define FXAA_FAST_PIXEL_OFFSET 1 // With OpenGL 3.2 we can always do this
@@ -509,12 +500,8 @@ layout(std140, set = 2, binding = 0) uniform genericData {
#else
uniform sampler2D tex0;
in vec2 v_rcpFrame;
#ifdef GL_ES
in vec2 v_pos;
#else
noperspective in vec2 v_pos;
#endif
layout(location=0)out vec4 fragOut0;
out vec4 fragOut0;
#endif
void main() {
-7
View File
@@ -1,6 +1,3 @@
#ifdef GL_ES
precision highp float;
#endif
#ifdef VULKAN
layout(location = 0) out vec2 fragTexCoord;
@@ -13,11 +10,7 @@ void main() {
in vec4 vertPosition;
out vec2 v_rcpFrame;
#ifdef GL_ES
out vec2 v_pos;
#else
noperspective out vec2 v_pos;
#endif
layout (std140) uniform genericData {
float rt_w;
+2 -8
View File
@@ -1,12 +1,6 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2D;
#endif
layout(location=0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
// [0]=real color (passed through as-is), [1]=luma detection proxy (see
// getAADetectionView; may be the same view as [0] when not HDR)
layout(set = 1, binding = 1) uniform sampler2D fxaaTex[2];
@@ -14,9 +8,9 @@ layout(set = 1, binding = 1) uniform sampler2D fxaaTex[2];
#define lumaTex fxaaTex[1]
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
#endif
void main() {
vec4 color = texture(tex, fragTexCoord.xy);
#ifdef VULKAN
@@ -1,17 +1,12 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2D;
#endif
#include "gamma.sdr"
layout(location=0)out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
#endif
-3
View File
@@ -1,6 +1,3 @@
#ifdef GL_ES
precision highp float;
#endif
const float SRGB_GAMMA = 2.2;
const float SRGB_GAMMA_INVERSE = 1.0 / SRGB_GAMMA;
+6 -12
View File
@@ -1,21 +1,15 @@
//!#version 150
#ifdef GL_ES
precision highp float;
precision highp int;
precision highp samplerCube;
#endif
#include "gamma.sdr"
#include "gamma.sdr" //! #include "gamma.sdr"
#define PI 3.1415926535897932384626433832795
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform samplerCube envmap;
#else
uniform samplerCube envmap;
in vec4 fragTexCoord;
out vec4 fragOut0;
#endif
#ifdef VULKAN
@@ -160,8 +154,8 @@ vec3( 0.7305, 0.6698, 0.1333)
);
void main() {
float xpos = 2.0 * gl_FragCoord.x/16.0 - 1.0; // remap to (-1, 1)
float ypos = 2.0 * gl_FragCoord.y/16.0 - 1.0;
float xpos = 2 * gl_FragCoord.x/16 - 1; // remap to (-1, 1)
float ypos = 2 * gl_FragCoord.y/16 - 1;
vec3 fragDir;
vec4 fragCol = vec4(0.0);
@@ -197,5 +191,5 @@ void main() {
vec3 sampDir = points[i] + (aPdotD - PdotD) * fragDir;
fragCol += aPdotD * vec4(srgb_to_linear(texture(envmap, sampDir)).rgb, 1.0);
}
fragOut0 = linear_to_srgb(PI * fragCol * (1.0/128.0));
fragOut0 = linear_to_srgb(PI * fragCol * (1.0/128));
}
+2 -2
View File
@@ -57,7 +57,7 @@ vec3 ComputeGGX(vec3 specColor, vec3 diffColor, vec3 light, vec3 normal, vec3 ha
vec3 specular = distribution * fresnel * geometry / (4.0*dotNV*dotNL + 0.0001);
vec3 specular = distribution * fresnel * geometry / (4*dotNV*dotNL + 0.0001);
// Diffuse term - Lambertian, kD represents energy lost to specular reflection.
vec3 kD = vec3(1.0)-fresnel;
@@ -70,7 +70,7 @@ vec3 ComputeGGX(vec3 specColor, vec3 diffColor, vec3 light, vec3 normal, vec3 ha
vec3 computeLighting(vec3 specColor, vec3 diffColor, vec3 light, vec3 normal, vec3 halfVec, vec3 view, float roughness, float fresnelFactor, float dotNL)
{
#ifdef FLAG_LIGHT_MODEL_BLINN_PHONG
return SpecularBlinnPhong(specColor, light, normal, halfVec, 32.0, fresnelFactor, dotNL);
return SpecularBlinnPhong(specColor, light, normal, halfVec, 32, fresnelFactor, dotNL);
#else
return ComputeGGX(specColor, diffColor, light, normal, halfVec, view, roughness, fresnelFactor, dotNL);
#endif
+3 -8
View File
@@ -1,14 +1,7 @@
#ifdef GL_ES
precision highp float;
precision highp int;
precision highp sampler2D;
#endif
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D textures[16];
#define scene textures[0]
@@ -16,6 +9,8 @@ layout(set = 1, binding = 1) uniform sampler2D textures[16];
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D scene;
uniform sampler2D cockpit;
#endif
+14 -31
View File
@@ -1,13 +1,4 @@
//? #version 150
#ifdef GL_ES
#extension GL_EXT_clip_cull_distance: enable
precision highp float;
precision highp int;
precision highp sampler2D;
precision highp sampler2DArray;
precision highp sampler2DArrayShadow;
#endif
#include "shadows.sdr" //! #include "shadows.sdr"
#include "lighting.sdr" //! #include "lighting.sdr"
#include "gamma.sdr" //! #include "gamma.sdr"
@@ -121,13 +112,6 @@ uniform shadowCascadeParams {
vec4 smoothness_factors[(NUM_SHADOW_CASCADES + 4 - 1) / 4];
};
layout(location = 0) out vec4 fragOut0;
layout(location = 1) out vec4 fragOut1;
layout(location = 2) out vec4 fragOut2;
layout(location = 3) out vec4 fragOut3;
layout(location = 4) out vec4 fragOut4;
#ifdef VULKAN
layout(set = 1, binding = 1) uniform sampler2DArray materialTextures[16];
#define sBasemap materialTextures[0]
@@ -155,12 +139,13 @@ layout(location = 0) in VertexOutput {
vec4 shadowUV[NUM_SHADOW_CASCADES];
vec4 shadowPos;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
float vClipDist;
#endif
} vertIn;
layout(location = 0) out vec4 fragOut0;
layout(location = 1) out vec4 fragOut1;
layout(location = 2) out vec4 fragOut2;
layout(location = 3) out vec4 fragOut3;
layout(location = 4) out vec4 fragOut4;
#else
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_DIFFUSE
uniform sampler2DArray sBasemap;
@@ -200,10 +185,13 @@ in VertexOutput {
vec4 shadowUV[NUM_SHADOW_CASCADES];
vec4 shadowPos;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
float vClipDist;
#endif
} vertIn;
out vec4 fragOut0;
out vec4 fragOut1;
out vec4 fragOut2;
out vec4 fragOut3;
out vec4 fragOut4;
#endif
vec3 FresnelLazarovEnv(vec3 specColor, vec3 view, vec3 normal, float gloss)
@@ -295,10 +283,6 @@ vec3 CalculateLighting(vec3 normal, vec3 diffuseMaterial, vec3 specularMaterial,
void main()
{
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
if (vertIn.vClipDist < 0.0) discard;
#endif
vec3 eyeDir = vec3(normalize(-vertIn.position).xyz);
vec2 texCoord = vertIn.texCoord.xy;
@@ -480,7 +464,7 @@ void main()
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_DIFFUSE
// This code will only be used by the forward renderer so we apply the fog effect to both the emissive and the base
// color.
float fogDensityFinal = clamp(1.0 - pow(fogDensity, vertIn.fogDist) , 0.0, 1.0);
float fogDensityFinal = clamp(1 - pow(fogDensity, vertIn.fogDist) , 0.0, 1.0);
baseColor.rgb = mix(emissiveColor.rgb + baseColor.rgb, finalFogColor, fogDensityFinal);
emissiveColor.rgb = vec3(0.0); // Zero the emissive color since it has already been added by the previous line
specColor.rgb *= fogDensityFinal;
@@ -525,12 +509,11 @@ void main()
#prereplace IF_NOT_FLAG MODEL_SDR_FLAG_LIGHT
#prereplace IF_FLAG MODEL_SDR_FLAG_SPEC
baseColor.rgb += pow(1.0 - clamp(dot(eyeDir, normal), 0.0, 1.0), 5.0 * clamp(glossData, 0.01, 1.0)) * specColor.rgb;
glossData = 0.0f;
glossData = 0;
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SPEC
// If there is no lighting then we copy the color data so far into the emissive.
emissiveColor.rgb += baseColor.rgb;
aoFactors.x = 0.0f;
aoFactors.x = 0;
// and leave nothing for the light pass to add on top of it
baseColor.rgb = vec3(0.0);
specColor = vec4(0.0);
+1 -26
View File
@@ -1,11 +1,4 @@
#ifdef GL_ES
#extension GL_EXT_gpu_shader5: enable
#extension GL_EXT_clip_cull_distance: enable
precision highp float;
precision highp int;
#else
#extension GL_ARB_gpu_shader5: enable
#endif
#conditional_include +"LARGE_SHADER" "main_large.sdr"
#conditional_include -"LARGE_SHADER" "main_small.sdr"
@@ -107,9 +100,6 @@ in VertexOutput {
vec4 shadowUV[NUM_SHADOW_CASCADES];
vec4 shadowPos;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
float vClipDist;
#endif
} vertIn[];
out VertexOutput {
@@ -127,10 +117,6 @@ out VertexOutput {
vec4 shadowUV[NUM_SHADOW_CASCADES];
vec4 shadowPos;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
float vClipDist;
#endif
} vertOut;
#ifdef MODEL_SDR_FLAG_THICK_OUTLINES
@@ -180,19 +166,8 @@ out VertexOutput {
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SHADOWS
if (use_clip_plane != 0) {
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
vertOut.vClipDist = vertIn[vert].vClipDist;
#else
gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0];
#endif
} else {
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
vertOut.vClipDist = 1.0;
#else
gl_ClipDistance[0] = 1.0;
#endif
gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0];
}
EmitVertex();
}
+6 -32
View File
@@ -1,18 +1,8 @@
#ifndef VULKAN
//? #version 150
#ifdef GL_ES
#extension GL_EXT_gpu_shader5: enable
#extension GL_EXT_clip_cull_distance: enable
precision highp float;
precision highp int;
precision highp samplerBuffer;
precision highp sampler2DArrayShadow;
#else
#extension GL_ARB_gpu_shader5: enable
#endif
#endif
#include "shadows.sdr"
#conditional_include +"LARGE_SHADER" "main_large.sdr"
@@ -155,10 +145,6 @@ layout(location = 0) out VertexOutput {
vec4 shadowUV[NUM_SHADOW_CASCADES];
vec4 shadowPos;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
float vClipDist;
#endif
} vertOut;
#else
#prereplace IF_FLAG_COMPILED MODEL_SDR_FLAG_TRANSFORM
@@ -180,10 +166,6 @@ out VertexOutput {
vec4 shadowUV[NUM_SHADOW_CASCADES];
vec4 shadowPos;
#prereplace ENDIF_FLAG_COMPILED MODEL_SDR_FLAG_SHADOWS
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
float vClipDist;
#endif
} vertOut;
#define TEXELS_PER_MATRIX 4
@@ -265,20 +247,12 @@ void main()
}
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_TRANSFORM
#ifndef SDR_FLAG_SW_CLIP_FALLBACK
if (use_clip_plane != 0) {
gl_ClipDistance[0] = dot(clip_equation, modelMatrix * orient * vertex);
} else {
gl_ClipDistance[0] = 1.0;
}
#else
if (use_clip_plane != 0) {
vertOut.vClipDist = dot(clip_equation, modelMatrix * orient * vertex);
} else {
vertOut.vClipDist = 1.0;
}
#endif
// Clip plane
if(use_clip_plane != 0) {
gl_ClipDistance[0] = dot(clip_equation, modelMatrix * orient * vertex);
} else {
gl_ClipDistance[0] = 1.0;
}
vertOut.position = position;
vertOut.normal = normal;
+12 -13
View File
@@ -1,20 +1,14 @@
#ifdef GL_ES
precision highp float;
precision highp int;
precision highp sampler2DMS;
#endif
#include "z-compress.sdr"
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(location = 1) out vec4 fragOut1;
layout(location = 2) out vec4 fragOut2;
layout(location = 3) out vec4 fragOut3;
layout(location = 4) out vec4 fragOut4;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(set = 1, binding = 1) uniform sampler2DMS msaaTex[6];
#define texColor msaaTex[0]
#define texPos msaaTex[1]
@@ -29,7 +23,12 @@ layout(std140, set = 2, binding = 0) uniform genericData {
};
#else
in vec4 fragTexCoord;
layout(location=5)out vec4 fragOut5;
out vec4 fragOut0;
out vec4 fragOut1;
out vec4 fragOut2;
out vec4 fragOut3;
out vec4 fragOut4;
out vec4 fragOut5;
uniform sampler2DMS texColor;
uniform sampler2DMS texPos;
@@ -177,7 +176,7 @@ float getMedianDist(ivec2 texel) {
//Fallback, do front first
float getMedianDist(ivec2 texel) {
float minDist = -1000000.0;
float minDist = -1000000;
for(int i = 0; i < samples; i++) {
minDist = max(minDist, uncompress_depth_value(texelFetch(texPos, texel, i).z));
}
@@ -191,7 +190,7 @@ float getMedianDist(ivec2 texel) {
void main()
{
vec2 texSize = vec2(textureSize(texColor));
vec2 texSize = textureSize(texColor);
#ifdef VULKAN
ivec2 texel = ivec2(texSize * fragTexCoord);
#else
@@ -207,7 +206,7 @@ void main()
vec4 normal = vec4(0);
vec4 specular = vec4(0);
vec4 emissive = vec4(0);
float depth = 0.0;
float depth = 0;
for(int i = 0; i < SAMPLE_CNT; i++) {
vec4 localPos = texelFetch(texPos, texel, i);
-5
View File
@@ -1,8 +1,3 @@
#ifdef GL_ES
precision highp float;
precision highp int;
precision highp sampler2DArray;
#endif
#ifdef VULKAN
layout (location = 0) in vec2 ftcoord;
-4
View File
@@ -1,7 +1,3 @@
#ifdef GL_ES
precision highp float;
precision highp int;
#endif
#ifdef VULKAN
layout (location = 0) in vec4 vertPosition;
+1 -1
View File
@@ -7,6 +7,6 @@
vec3 unpackNormal(vec2 normal_sample) {
vec3 normal;
normal.xy = normal_sample * 2.0 - 1.0;
normal.z = sqrt(1.0 - normal_sample.x * normal_sample.x - normal_sample.y * normal_sample.y);
normal.z = sqrt(1 - normal_sample.x * normal_sample.x - normal_sample.y * normal_sample.y);
return normal;
}
@@ -1,14 +1,9 @@
#ifdef GL_ES
precision highp float;
precision highp int;
precision highp sampler2D;
#endif
#include "gamma.sdr"
in vec4 fragTexCoord;
in vec4 fragColor;
layout(location=0) out vec4 fragOut0;
out vec4 fragOut0;
uniform sampler2D baseMap;
+2 -7
View File
@@ -1,16 +1,11 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2D;
#endif
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
layout(set = 1, binding = 4) uniform sampler2D depth_tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
uniform sampler2D depth_tex;
#endif
+3 -8
View File
@@ -1,17 +1,10 @@
#ifdef GL_ES
precision highp float;
precision highp int;
precision highp sampler2DArray;
#endif
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 1) in vec4 fragColor;
layout(location = 2) in vec2 fragScreenPosition;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2DArray baseMap;
#else
@@ -19,6 +12,8 @@ in vec2 fragTexCoord;
in vec4 fragColor;
in vec2 fragScreenPosition;
out vec4 fragOut0;
uniform sampler2DArray baseMap;
#endif
@@ -1,7 +1,3 @@
#ifdef GL_ES
precision highp float;
precision highp int;
#endif
#ifdef VULKAN
layout(location = 0) in vec2 vertPosition;
@@ -1,24 +1,5 @@
//? #version 150
#ifdef GL_ES
#extension GL_EXT_clip_cull_distance: enable
precision highp float;
#endif
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
#ifdef VULKAN
layout(location = 0) in VertexOutput {
float vClipDist;
} vertIn;
#else
in VertexOutput {
float vClipDist;
} vertIn;
#endif
#endif
void main()
{
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
if (vertIn.vClipDist < 0.0) discard;
#endif
}
+4 -25
View File
@@ -1,16 +1,9 @@
//? #version 150
#ifdef GL_ES
#extension GL_EXT_gpu_shader5: enable
#extension GL_EXT_clip_cull_distance: enable
precision highp float;
precision highp int;
#else
#extension GL_ARB_gpu_shader5: enable
#endif
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
#if defined(GL_ARB_gpu_shader5) || defined(GL_EXT_gpu_shader5)
#ifdef GL_ARB_gpu_shader5
layout(invocations = NUM_SHADOW_CASCADES) in;
#endif
@@ -31,25 +24,16 @@ layout (std140) uniform shadowCascadeParams {
};
in VertexOutput {
#if !defined(GL_ARB_gpu_shader5) && !defined(GL_EXT_gpu_shader5)
#if !defined(GL_ARB_gpu_shader5)
float instance;
#endif
float clipModel;
vec3 normal;
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
float vClipDist;
#endif
} vertIn[];
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
out VertexOutput {
float vClipDist;
} vertOut;
#endif
void main(void)
{
#if defined(GL_ARB_gpu_shader5) || defined(GL_EXT_gpu_shader5)
#ifdef GL_ARB_gpu_shader5
if (gl_InvocationID >= cascade_count) return;
int cascade_id = cascade_offset + gl_InvocationID;
#else
@@ -65,12 +49,7 @@ void main(void)
}
gl_Layer = cascade_id;
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
vertOut.vClipDist = vertIn[vert].vClipDist;
#else
gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0];
#endif
gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0];
EmitVertex();
}
+3 -31
View File
@@ -1,16 +1,9 @@
//? #version 150
#ifdef GL_ES
#extension GL_EXT_gpu_shader5: enable
#extension GL_EXT_clip_cull_distance: enable
precision highp float;
precision highp int;
precision highp samplerBuffer;
#else
#extension GL_ARB_gpu_shader5: enable
#ifndef GEOMETRY_FALLBACK
#extension GL_ARB_shader_viewport_layer_array : enable
#endif
#endif
#ifdef VULKAN
layout(location = 0) in vec4 vertPosition;
@@ -73,25 +66,12 @@ uniform samplerBuffer transform_tex;
// supports writing gl_Layer from the vertex shader, see CAPABILITY_FAST_SHADOWS).
#ifdef GEOMETRY_FALLBACK
out VertexOutput {
#if !defined(GL_ARB_gpu_shader5) && !defined(GL_EXT_gpu_shader5)
#if !defined(GL_ARB_gpu_shader5)
float instance;
#endif
float clipModel;
vec3 normal;
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
float vClipDist;
#endif
} vertOut;
#elif defined(SDR_FLAG_SW_CLIP_FALLBACK)
#ifdef VULKAN
layout(location = 0) out VertexOutput {
float vClipDist;
} vertOut;
#else
out VertexOutput {
float vClipDist;
} vertOut;
#endif
#endif
#define TEXELS_PER_MATRIX 4
@@ -132,7 +112,7 @@ void main()
#ifdef VULKAN
int instanceIndex = gl_InstanceIndex;
#else
#if !defined(GL_ARB_gpu_shader5) && !defined(GL_EXT_gpu_shader5)
#if !defined(GL_ARB_gpu_shader5)
#ifdef APPLE
vertOut.instance = float(gl_InstanceIDARB);
#else
@@ -153,19 +133,11 @@ void main()
gl_Position = position;
}
#ifndef SDR_FLAG_SW_CLIP_FALLBACK
if (use_clip_plane != 0) {
gl_ClipDistance[0] = dot(clip_equation, modelMatrix * orient * vertex);
} else {
gl_ClipDistance[0] = 1.0;
}
#else
if (use_clip_plane != 0) {
vertOut.vClipDist = dot(clip_equation, modelMatrix * orient * vertex);
} else {
vertOut.vClipDist = 1.0;
}
#endif
#ifdef GEOMETRY_FALLBACK
vertOut.normal = normal;
@@ -1,22 +1,21 @@
#ifdef GL_ES
precision highp float;
precision highp int;
precision highp sampler2DArray;
#endif
#include "gamma.sdr"
const float EMISSIVE_GAIN = 2.0;
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec4 fragImpactUV;
layout(location = 1) in float fragNormOffset;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2DArray shieldMap;
#else
in vec4 fragImpactUV;
in float fragNormOffset;
out vec4 fragOut0;
uniform sampler2DArray shieldMap;
#endif
@@ -1,7 +1,3 @@
#ifdef GL_ES
precision highp float;
precision highp int;
#endif
#ifdef VULKAN
layout(location = 0) in vec4 vertPosition;
+4 -6
View File
@@ -1,7 +1,3 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2D;
#endif
#ifdef VULKAN
layout(std140, set = 2, binding = 0) uniform genericData {
@@ -20,13 +16,13 @@ layout (std140) uniform genericData {
#include "SMAA.sdr"
layout(location=0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 1) in vec2 fragPixcoord;
layout(location = 2) in vec4 fragOffset[3];
layout(location = 0) out vec4 fragOut0;
// [0]=edgesTex, [1]=areaTex, [2]=searchTex
layout(set = 1, binding = 1) uniform sampler2D smaaTex[3];
#define edgesTex smaaTex[0]
@@ -37,6 +33,8 @@ in vec2 fragTexCoord;
in vec2 fragPixcoord;
in vec4 fragOffset[3];
out vec4 fragOut0;
uniform sampler2D edgesTex;
uniform sampler2D areaTex;
uniform sampler2D searchTex;
+4 -6
View File
@@ -1,7 +1,3 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2D;
#endif
#ifdef VULKAN
layout(std140, set = 2, binding = 0) uniform genericData {
@@ -20,17 +16,19 @@ layout (std140) uniform genericData {
#include "SMAA.sdr"
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 1) in vec4 fragOffset[3];
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D colorTex;
#else
in vec2 fragTexCoord;
in vec4 fragOffset[3];
out vec4 fragOut0;
uniform sampler2D colorTex;
#endif
@@ -1,7 +1,3 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2D;
#endif
#ifdef VULKAN
layout(std140, set = 2, binding = 0) uniform genericData {
@@ -20,12 +16,11 @@ layout (std140) uniform genericData {
#include "SMAA.sdr"
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 1) in vec4 fragOffset;
layout(location = 0) out vec4 fragOut0;
// [0]=colorTex, [1]=blendTex
layout(set = 1, binding = 1) uniform sampler2D smaaTex[2];
@@ -35,6 +30,8 @@ layout(set = 1, binding = 1) uniform sampler2D smaaTex[2];
in vec2 fragTexCoord;
in vec4 fragOffset;
out vec4 fragOut0;
uniform sampler2D colorTex;
uniform sampler2D blendTex;
#endif
@@ -1,15 +1,10 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2D;
#endif
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
#endif
@@ -1,18 +1,13 @@
#ifdef GL_ES
precision highp float;
precision highp int;
precision highp sampler2D;
#endif
#include "gamma.sdr"
layout(location=0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2D tex;
#endif
+2 -7
View File
@@ -1,12 +1,6 @@
#ifdef GL_ES
precision highp float;
precision highp sampler2DArray;
#endif
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec4 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2DArray textures[16];
#define ytex textures[0]
@@ -14,6 +8,7 @@ layout(set = 1, binding = 1) uniform sampler2DArray textures[16];
#define vtex textures[2]
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
uniform sampler2DArray ytex;
uniform sampler2DArray utex;
uniform sampler2DArray vtex;
+13 -28
View File
@@ -1,14 +1,7 @@
#ifdef GL_ES
precision highp float;
precision highp int;
precision highp sampler2D;
precision highp sampler3D;
#endif
layout(location = 0) out vec4 fragOut0;
#ifdef VULKAN
layout(location = 0) in vec2 fragTexCoord;
layout(location = 0) out vec4 fragOut0;
layout(set = 1, binding = 1) uniform sampler2D tex2D[16];
#define composite tex2D[0]
#define emissive tex2D[1]
@@ -17,7 +10,7 @@ layout(set = 1, binding = 5) uniform sampler3D volume_tex;
layout(set = 1, binding = 6) uniform sampler3D noise_volume_tex;
#else
in vec4 fragTexCoord;
out vec4 fragOut0;
layout (binding = 0) uniform sampler2D composite;
layout (binding = 1) uniform sampler2D emissive;
layout (binding = 2) uniform sampler2D depth_tex;
@@ -84,7 +77,7 @@ void main()
#else
vec4 eyeDirection = p_inv * vec4(fragTexCoord.xy * 2.0 - 1.0, -1, 1);
#endif
eyeDirection.w = 0.0;
eyeDirection.w = 0;
vec3 rayDirection = normalize((v_inv * eyeDirection).xyz);
vec4 color_in = texture(composite, fragTexCoord.xy);
@@ -110,14 +103,14 @@ void main()
#endif
//The t at which the ray enters the nebula cube
float maxtMin = max(0.0, max(tMin.x, max(tMin.y, tMin.z)));
float maxtMin = max(0,max(tMin.x, max(tMin.y, tMin.z)));
//The t at which the ray leaves the nebula cube
float mintMax = min(fragDepth, min(tMax.x, min(tMax.y, tMax.z)));
//The cumulative one minus alpha value of the nebula. Corresponds to the multiplier of the current step when additively drawing to a fragment for which the steps closer to the camera are already drawn.
float cumOMAlpha = 1.0;
float cumOMAlpha = 1;
//The cumulative distance already travelled through the nebula.
float cumnebdist = 0.0;
float cumnebdist = 0;
//The color of the current fragment
vec3 cumcolor = vec3(0, 0, 0);
@@ -128,14 +121,6 @@ void main()
vec3 sidestep = 1.0 / vec3(textureSize(volume_tex, 0));
#ifdef GL_ES
// The emissive texture has a complete mip chain generated by the engine.
ivec2 emissiveSize = textureSize(emissive, 0);
float maxEmissiveLod = float(findMSB(max(emissiveSize.x, emissiveSize.y)));
#else
float maxEmissiveLod = float(textureQueryLevels(emissive) - 1);
#endif
for(float stept = maxtMin; stept < mintMax;) {
//Step setup
vec3 position = camera + rayDirection * stept - nebPos;
@@ -174,7 +159,7 @@ void main()
#ifdef NOISE
//Mix the actual nebula color with the noise color depending on the product of the noise channels multiplied with intensity, smooth-clamped from 0 to 1
vec3 stepcolor_neb = mix(nebulaColor, noiseColor,
smoothstep(0.0, 1.0, (textureGrad(noise_volume_tex, position / noiseColorScale1, gradX, gradY).r + textureGrad(noise_volume_tex, position / noiseColorScale2, gradX, gradY).g) / 2.0 * noiseIntensity));
smoothstep(0, 1, (textureGrad(noise_volume_tex, position / noiseColorScale1, gradX, gradY).r + textureGrad(noise_volume_tex, position / noiseColorScale2, gradX, gradY).g) / 2.0 * noiseIntensity));
#else
vec3 stepcolor_neb = nebulaColor;
#endif
@@ -184,19 +169,19 @@ void main()
//From the current position in the volumetric cloud, sample towards the sun to test whether or not the current part of the nebula is bright or not
for(int dlstep = 1; dlstep <= directionalLightSampleSteps; dlstep++){
//TODO Sample these in a cone towards the sun, not just a line. Also add the high-distance sample for concave nebulae
vec3 dlsteppos = (position - globalLightDirection * (float(dlstep) * directionalLightStepSize)) / nebSize + 0.5;
float dlstepalpha = textureGrad(volume_tex, dlsteppos, gradX, gradY).a * step(0.0, dlsteppos.x) * step(dlsteppos.x, 1.0) * step(0.0, dlsteppos.y) * step(dlsteppos.y, 1.0) * step(0.0, dlsteppos.z) * step(dlsteppos.z, 1.0);
vec3 dlsteppos = (position - globalLightDirection * (dlstep * directionalLightStepSize)) / nebSize + 0.5;
float dlstepalpha = textureGrad(volume_tex, dlsteppos, gradX, gradY).a * step(0, dlsteppos.x) * step(dlsteppos.x, 1) * step(0, dlsteppos.y) * step(dlsteppos.y, 1) * step(0, dlsteppos.z) * step(dlsteppos.z, 1);
directionalLightDepth += dlstepalpha * directionalLightStep;
}
stepcolor_diffuse *= beer_powder_norm * (1.0 - exp(-directionalLightDepth * 2.0)) * exp(-directionalLightDepth);
stepcolor_diffuse *= beer_powder_norm * (1 - exp(-directionalLightDepth * 2.0)) * exp(-directionalLightDepth);
//Emissive
cumnebdist += stepcolor_alpha * stepsize_current;
vec3 emissive_lod = textureLod(emissive, fragTexCoord.xy, clamp(cumnebdist * emissiveSpreadFactor, 0.0, maxEmissiveLod)).rgb;
vec3 stepcolor_emissive = clamp(emissive_lod.rgb * pow(alphalim, 1.0 / (opacitydistance / ((fragDepth - stept) * emissiveFalloff + 0.01))) * emissiveIntensity, 0.0, 1.0);
vec3 emissive_lod = textureLod(emissive, fragTexCoord.xy, clamp(cumnebdist * emissiveSpreadFactor, 0, float(textureQueryLevels(emissive) - 1))).rgb;
vec3 stepcolor_emissive = clamp(emissive_lod.rgb * pow(alphalim, 1.0 / (opacitydistance / ((fragDepth - stept) * emissiveFalloff + 0.01))) * emissiveIntensity, 0, 1);
//Step finish
vec3 stepcolor = clamp(stepcolor_diffuse + stepcolor_emissive, 0.0, 1.0);
vec3 stepcolor = clamp(stepcolor_diffuse + stepcolor_emissive, 0, 1);
cumcolor += stepalpha * cumOMAlpha * stepcolor;
}
-17
View File
@@ -28,23 +28,6 @@ protected:
return FALSE;
m_library = SDL_LoadObject(externlib);
#ifdef __ANDROID__
if (m_library == NULL) {
auto android_path = SDL_GetAndroidInternalStoragePath();
if (android_path != nullptr) {
SCP_string android_full_path = android_path;
if (android_full_path.back() != DIR_SEPARATOR_CHAR) {
android_full_path += DIR_SEPARATOR_CHAR;
}
android_full_path += SCP_string("natives") + DIR_SEPARATOR_CHAR + externlib;
#ifndef NDEBUG
mprintf(("Calling SDL_LoadObject with the following path: '%s'\n", android_full_path.c_str()));
#endif
m_library = SDL_LoadObject(android_full_path.c_str());
}
}
#endif
// check full path as a fallback for our own libraries
// NOTE: basePath is assumed to have a trailing slash!
+12 -77
View File
@@ -22,7 +22,6 @@
#include "material.h"
#include "matrix.h"
#include "bmpman/bmpman.h"
#include "cmdline/cmdline.h"
#include "debugconsole/console.h"
#include "executor/global_executors.h"
@@ -43,7 +42,6 @@
#include "render/3d.h"
#include "scripting/hook_api.h"
#include "scripting/scripting.h"
#include "tracing/ProfilerOverlay.h"
#include "tracing/tracing.h"
#include "utils/boost/hash_combine.h"
#include "utils/string_utils.h"
@@ -291,11 +289,7 @@ GraphicsAPI gr_get_configured_render_api()
#endif
#ifndef __ANDROID__
os::ViewportState Gr_configured_window_state = os::ViewportState::Fullscreen;
#else
os::ViewportState Gr_configured_window_state = os::ViewportState::Borderless;
#endif
static bool mode_change_func(os::ViewportState state, bool initial)
{
@@ -3265,38 +3259,24 @@ void gr_set_bitmap(int bitmap_num, int alphablend_mode, int bitblt_mode, float a
gr_screen.current_bitmap = bitmap_num;
}
gr_debug_stats gr_get_debug_stats()
static void output_uniform_debug_data()
{
gr_debug_stats stats;
if (!Cmdline_graphics_debug_output) {
return stats;
if (gr_screen.mode == GraphicsAPI::Stub) {
return;
}
if (UniformBufferManager) {
stats.uniform_buffer_valid = true;
stats.uniform_buffer_size = UniformBufferManager->getBufferSize();
stats.uniform_buffer_used = UniformBufferManager->getCurrentlyUsedSize();
}
int line_height = gr_get_font_height() + 1;
gr_screen.gf_get_debug_stats(stats);
gr_set_color_fast(&Color_bright_white);
return stats;
gr_printf_no_resize(gr_screen.center_offset_x + 20, gr_screen.center_offset_y + 160,
"Uniform buffer size: " SIZE_T_ARG, UniformBufferManager->getBufferSize());
gr_printf_no_resize(gr_screen.center_offset_x + 20, gr_screen.center_offset_y + 160 + line_height,
"Currently used data: " SIZE_T_ARG, UniformBufferManager->getCurrentlyUsedSize());
}
static bool Imgui_frame_active = false;
void gr_imgui_begin_frame()
{
if (Imgui_frame_active) {
return;
}
// The stub renderer (standalone server) never assigns the ImGui entry points.
if (!gr_screen.gf_imgui_new_frame || !ImGui::GetCurrentContext()) {
return;
}
gr_imgui_new_frame(); // renderer backend (OpenGL/Vulkan)
ImGui_ImplSDL3_NewFrame(); // platform backend, derives the display size from the SDL window
@@ -3314,23 +3294,6 @@ void gr_imgui_begin_frame()
}
ImGui::NewFrame();
Imgui_frame_active = true;
}
void gr_imgui_end_frame()
{
if (!Imgui_frame_active) {
return;
}
ImGui::Render();
gr_imgui_render_draw_data();
Imgui_frame_active = false;
}
bool gr_imgui_frame_active()
{
return Imgui_frame_active;
}
void gr_flip(bool execute_scripting)
@@ -3351,13 +3314,9 @@ void gr_flip(bool execute_scripting)
model_process_cached_ui_render_instances();
// Every presented frame drains the frame profiler and contributes the overlay window to
// this frame's ImGui pass. Doing it here rather than per game state is what keeps the
// profiler's event buffer bounded: collection is global, so the drain has to be too.
tracing::profiler_overlay_frame();
// Closes whatever ImGui frame the overlay (or the lab, or the options screen) opened.
gr_imgui_end_frame();
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
@@ -3626,30 +3585,6 @@ void gr_heap_deallocate(GpuHeap heap_type, size_t data_offset)
gpuHeap->freeGpuData(data_offset);
}
gr_memory_stats gr_get_memory_stats()
{
gr_memory_stats stats;
// gpu_heaps[] entries stay null when gpu_heap_init() early-returned (GraphicsAPI::Stub, e.g.
// a build with both graphics backends disabled), so this must not assume a live heap.
auto vertex_heap = get_gpu_heap(GpuHeap::ModelVertex);
auto index_heap = get_gpu_heap(GpuHeap::ModelIndex);
if (vertex_heap != nullptr && index_heap != nullptr) {
stats.model_heap_valid = true;
stats.model_vertex_heap_used = vertex_heap->usedBytes();
stats.model_vertex_heap_size = vertex_heap->bufferSize();
stats.model_index_heap_used = index_heap->usedBytes();
stats.model_index_heap_size = index_heap->bufferSize();
}
stats.locked_bitmap_ram_valid = true;
stats.locked_bitmap_ram_bytes = bm_texture_ram;
gr_screen.gf_get_memory_stats(stats);
return stats;
}
void gr_set_gamma(float gamma)
{
if (gr_screen.mode == GraphicsAPI::Stub) {
-85
View File
@@ -690,56 +690,6 @@ enum class BufferUsageHint { Static, Dynamic, Streaming, PersistentMapping };
*/
typedef void* gr_sync;
/**
* @brief Per-backend diagnostic counters, populated only when -gr_debug is active
*
* Each stat group carries its own "valid" flag since not every backend collects every
* group (e.g. per-draw-call counters currently only exist in the Vulkan backend). See
* gr_get_debug_stats().
*/
struct gr_debug_stats {
bool uniform_buffer_valid = false;
size_t uniform_buffer_size = 0;
size_t uniform_buffer_used = 0;
bool draw_stats_valid = false;
int draw_calls = 0;
int draw_indexed_calls = 0;
int total_vertices = 0;
int total_indices = 0;
int apply_material_calls = 0;
int apply_material_failures = 0;
int no_pipeline_skips = 0;
int descriptor_sets_allocated = 0;
int descriptor_writes = 0;
size_t pipeline_count = 0;
int on_demand_texture_uploads = 0;
};
/**
* @brief Cross-backend memory usage snapshot for the profiler overlay's memory panel
*
* Unlike gr_debug_stats, this is always populated when queried (no -gr_debug gate) -- memory
* usage is meant to be visible to anyone with the profiler overlay open. Each group still carries
* its own "valid" flag since not every backend/build collects every group (e.g. both graphics
* backends disabled at build time). See gr_get_memory_stats().
*/
struct gr_memory_stats {
bool model_heap_valid = false;
size_t model_vertex_heap_used = 0;
size_t model_vertex_heap_size = 0;
size_t model_index_heap_used = 0;
size_t model_index_heap_size = 0;
bool locked_bitmap_ram_valid = false;
size_t locked_bitmap_ram_bytes = 0;
bool gpu_purpose_valid = false;
size_t gpu_texture_bytes = 0;
size_t gpu_geometry_bytes = 0;
size_t gpu_render_target_bytes = 0;
};
typedef struct screen {
int max_w = 0, max_h = 0; // Width and height
int max_w_unscaled = 0, max_h_unscaled = 0;
@@ -1013,14 +963,6 @@ typedef struct screen {
std::function<void(const char* name)> gf_push_debug_group;
std::function<void()> gf_pop_debug_group;
// Fills in whichever debug_stats groups this backend collects. Defaults to a no-op
// so backends without per-draw-call counters (OpenGL, stub) don't have to assign it.
std::function<void(gr_debug_stats& stats)> gf_get_debug_stats = [](gr_debug_stats&) {};
// Fills in whichever memory_stats groups this backend collects (GPU-purpose byte totals).
// Defaults to a no-op so backends without per-purpose tagging don't have to assign it.
std::function<void(gr_memory_stats& stats)> gf_get_memory_stats = [](gr_memory_stats&) {};
std::function<int()> gf_create_query_object;
std::function<void(int obj, QueryType type)> gf_query_value;
std::function<bool(int obj)> gf_query_value_available;
@@ -1219,23 +1161,6 @@ bool gr_is_screenshot_requested();
//#define gr_flip GR_CALL(gr_screen.gf_flip)
void gr_flip(bool execute_scripting = true);
/**
* @brief Collects whichever graphics-API debug stats are available for the active backend
*
* Returns a default-constructed (all-invalid) gr_debug_stats unless -gr_debug is active. Safe
* to call every frame regardless of backend or debug flag.
*/
gr_debug_stats gr_get_debug_stats();
/**
* @brief Collects whichever cross-backend memory usage stats are available
*
* Unlike gr_get_debug_stats(), this is always populated (no -gr_debug gate) -- memory usage is
* meant to be visible whenever the profiler overlay is open. Safe to call every frame regardless
* of backend or build configuration; groups a backend/build doesn't support stay invalid/zero.
*/
gr_memory_stats gr_get_memory_stats();
inline void gr_setup_frame() {
gr_screen.gf_setup_frame();
}
@@ -1393,16 +1318,6 @@ inline void gr_post_process_restore_zbuffer()
*/
void gr_imgui_begin_frame();
/**
* @brief Renders and submits the open ImGui frame, if any. Called by gr_flip().
*/
void gr_imgui_end_frame();
/**
* @brief Whether an ImGui frame is currently open for contributions
*/
bool gr_imgui_frame_active();
inline void gr_render_primitives(material* material_info,
primitive_type prim_type,
vertex_layout* layout,
+3 -4
View File
@@ -32,7 +32,6 @@
#define GL_CLAMP_VERTEX_COLOR 0x891A
#define GL_CLAMP_FRAGMENT_COLOR 0x891B
#define GL_FIXED_ONLY 0x891D
#define GLAD_GL_ARB_shader_viewport_layer_array 0 // Disabled, no gl_layer in vertex shader //0x8DAE
//Enums Redefinitions
#define GL_CLIP_DISTANCE0 GL_CLIP_DISTANCE0_EXT // GL_EXT_clip_cull_distance
@@ -49,7 +48,6 @@
#define GLAD_GL_ARB_texture_storage GL_TRUE // ES 3.2
#define GLAD_GL_ARB_draw_buffers_blend GL_TRUE // ES 3.2 (glBlendFunci and glBlendEquationi)
#define GLAD_GL_ARB_vertex_attrib_binding GL_TRUE // ES 3.1
#define GL_DEPTH_CLAMP GL_DEPTH_CLAMP_EXT
//Functions Redefinitions
#define glBlendFunciARB glBlendFunci // ES 3.2
@@ -236,7 +234,9 @@ inline void glDrawBuffer(GLenum buf)
}
}
// ES uses layout(location = ) directly on shader
// does not exist on ES, ES uses layout(location=) directly on shader
// Notes:
// -This function is not required to run.
#define glBindFragDataLocation(program, colorNumber, name) ((void)0)
// glPolygonMode() is not supported on ES, no wireframe, GL_FILL is default, GL_POINTS, and GL_LINES needs an alternative path
@@ -254,7 +254,6 @@ static inline void glQueryCounter(GLuint id, GLenum target)
#else
(void)id;
#endif
(void)target;
}
// glGetCompressedTexImage not present on GLES and no equivalent
+3 -6
View File
@@ -6,7 +6,7 @@
#include <direct.h>
#endif
#if !defined __APPLE_CC__ && !defined __ANDROID__ && defined SCP_UNIX
#if !defined __APPLE_CC__ && defined SCP_UNIX
#include<glad/glad_glx.h>
//Required because X defines none and always, which is used later
#undef None
@@ -1164,7 +1164,6 @@ void gr_opengl_init_function_pointers()
gr_screen.gf_is_capable = gr_opengl_is_capable;
gr_screen.gf_get_property = gr_opengl_get_property;
gr_screen.gf_get_memory_stats = gr_opengl_get_memory_stats;
gr_screen.gf_push_debug_group = gr_opengl_push_debug_group;
gr_screen.gf_pop_debug_group = gr_opengl_pop_debug_group;
@@ -1395,7 +1394,7 @@ bool gr_opengl_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps)
Error(LOCATION, "Failed to load OpenGL!");
}
#if !defined __APPLE_CC__ && !defined __ANDROID__ && defined SCP_UNIX
#if !defined __APPLE_CC__ && defined SCP_UNIX
if (!gladLoadGLXLoader(GL_context->getLoaderFunction(), nullptr, 0)) {
Error(LOCATION, "Failed to load GLX!");
}
@@ -1454,9 +1453,7 @@ bool gr_opengl_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps)
"Deferred lightning is enabled in settings but the gpu does not support the minimum of 6 draw buffers. "
"This will result in rendering errors.");
}
mprintf((" Depth Clamp support : %s\n", GLAD_GL_EXT_depth_clamp ? NOX("YES") : NOX("NO")));
mprintf((" HW Cull distance support : %s\n", GLAD_GL_EXT_clip_cull_distance ? NOX("YES") : NOX("NO")));
mprintf((" Precompiled shaders support : %s\n", GLAD_GL_OES_get_program_binary ? NOX("YES") : NOX("NO")));
mprintf((" Immutable buffer storage support : %s\n", GLAD_GL_EXT_buffer_storage ? NOX("YES") : NOX("NO")));
mprintf((" BGRA8888 format support: %s\n", GLAD_GL_EXT_texture_format_BGRA8888 ? NOX("YES") : NOX("NO")));
-60
View File
@@ -760,66 +760,6 @@ void opengl_scene_texture_shutdown()
Scene_framebuffer_in_frame = false;
}
size_t opengl_get_scene_render_target_bytes()
{
// The bytes for each pixel come from the internal formats that opengl_setup_scene_textures()
// gives to these textures: GL_RGBA16F holds 8 bytes, GL_RGBA8 and GL_DEPTH24_STENCIL8 hold 4.
// Keep the values below equal to the formats at the creation sites in that function.
constexpr size_t RGBA16F_BYTES = 8;
constexpr size_t RGBA8_BYTES = 4;
constexpr size_t DEPTH24_STENCIL8_BYTES = 4;
size_t bytes = 0;
auto add_texture = [&bytes](GLuint texture_id, size_t width, size_t height, size_t bytes_per_pixel,
size_t samples = 1) {
if (texture_id == 0) {
return;
}
bytes += width * height * bytes_per_pixel * samples;
};
const auto scene_w = static_cast<size_t>(Scene_texture_width);
const auto scene_h = static_cast<size_t>(Scene_texture_height);
add_texture(Scene_color_texture, scene_w, scene_h, RGBA16F_BYTES);
add_texture(Scene_ldr_texture, scene_w, scene_h, RGBA8_BYTES);
add_texture(Scene_position_texture, scene_w, scene_h, RGBA16F_BYTES);
add_texture(Scene_normal_texture, scene_w, scene_h, RGBA16F_BYTES);
add_texture(Scene_specular_texture, scene_w, scene_h, RGBA8_BYTES);
add_texture(Scene_emissive_texture, scene_w, scene_h, RGBA16F_BYTES);
add_texture(Scene_composite_texture, scene_w, scene_h, RGBA16F_BYTES);
add_texture(Scene_luminance_texture, scene_w, scene_h, RGBA16F_BYTES);
add_texture(Cockpit_depth_texture, scene_w, scene_h, DEPTH24_STENCIL8_BYTES);
add_texture(Scene_depth_texture, scene_w, scene_h, DEPTH24_STENCIL8_BYTES);
// The multisample textures hold one value for each sample, thus their bytes go up with the
// MSAA level. Cmdline_msaa_enabled holds the number of samples and is 0 when MSAA is off.
const auto samples = static_cast<size_t>(std::max(1, Cmdline_msaa_enabled));
add_texture(Scene_color_texture_ms, scene_w, scene_h, RGBA16F_BYTES, samples);
add_texture(Scene_position_texture_ms, scene_w, scene_h, RGBA16F_BYTES, samples);
add_texture(Scene_normal_texture_ms, scene_w, scene_h, RGBA16F_BYTES, samples);
add_texture(Scene_specular_texture_ms, scene_w, scene_h, RGBA8_BYTES, samples);
add_texture(Scene_emissive_texture_ms, scene_w, scene_h, RGBA16F_BYTES, samples);
add_texture(Scene_depth_texture_ms, scene_w, scene_h, DEPTH24_STENCIL8_BYTES, samples);
// The back buffer and gamma blit textures use the size of the screen, not the (clamped) size
// of the scene textures.
const auto screen_w = static_cast<size_t>(gr_screen.max_w);
const auto screen_h = static_cast<size_t>(gr_screen.max_h);
add_texture(Back_texture, screen_w, screen_h, RGBA16F_BYTES);
add_texture(Back_depth_texture, screen_w, screen_h, DEPTH24_STENCIL8_BYTES);
add_texture(GammaBlit_texture, screen_w, screen_h, RGBA8_BYTES);
// The two distortion textures always have the size 32x32.
add_texture(Distortion_texture[0], 32, 32, RGBA8_BYTES);
add_texture(Distortion_texture[1], 32, 32, RGBA8_BYTES);
return bytes;
}
void gr_opengl_scene_texture_begin()
{
if ( !Scene_texture_initialized ) {
-9
View File
@@ -57,15 +57,6 @@ void gr_opengl_render_shield_impact(shield_material* material_info,
void opengl_setup_scene_textures();
void opengl_scene_texture_shutdown();
/**
* @brief Gives the bytes that the scene, backbuffer, and distortion textures hold
*
* The profiler overlay's memory panel adds this value to the "render targets" group. The function
* counts only the textures that exist at the moment of the call, thus it gives 0 before
* opengl_setup_scene_textures() runs and after opengl_scene_texture_shutdown() runs.
*/
size_t opengl_get_scene_render_target_bytes();
void gr_opengl_scene_texture_begin();
void gr_opengl_scene_texture_end();
void gr_opengl_copy_effect_texture();
+1 -1
View File
@@ -72,7 +72,7 @@ bool gr_opengl_openxr_test_capabilities() {
#ifdef WIN32
bool gr_opengl_openxr_create_session() {
auto hdc = static_cast<HDC>(SDL_GetPointerProperty(SDL_GetWindowProperties(os::getSDLMainWindow()),
SDL_PROP_WINDOW_WIN32_HDC_POINTER,
SDL_PROP_WINDOW_WIN32_HWND_POINTER,
nullptr));
XrGraphicsBindingOpenGLWin32KHR graphicsBinding{
@@ -1093,53 +1093,6 @@ void opengl_post_process_shutdown_bloom()
}
}
size_t opengl_get_postprocessing_render_target_bytes()
{
// The bytes for each pixel come from the internal formats at the creation sites in this file:
// GL_RGBA16F holds 8 bytes and GL_RGBA8 holds 4. Keep these values equal to those formats.
constexpr size_t RGBA16F_BYTES = 8;
constexpr size_t RGBA8_BYTES = 4;
size_t bytes = 0;
// The bloom textures are half the size of the post-processing textures. opengl_setup_bloom_textures()
// calls glGenerateMipmap() before it sets GL_TEXTURE_MAX_LEVEL, thus the driver holds the full
// mipmap chain, not only MAX_MIP_BLUR_LEVELS levels.
for (GLuint bloom_texture : Bloom_textures) {
if (bloom_texture == 0) {
continue;
}
auto width = static_cast<size_t>(std::max(1, Post_texture_width >> 1));
auto height = static_cast<size_t>(std::max(1, Post_texture_height >> 1));
while (true) {
bytes += width * height * RGBA16F_BYTES;
if ((width == 1) && (height == 1)) {
break;
}
width = std::max<size_t>(1, width >> 1);
height = std::max<size_t>(1, height >> 1);
}
}
// The SMAA area and search textures stay out of this total. They hold lookup data of a fixed
// small size, thus they are not render targets.
const auto post_w = static_cast<size_t>(Post_texture_width);
const auto post_h = static_cast<size_t>(Post_texture_height);
if (Smaa_edges_tex != 0) {
bytes += post_w * post_h * RGBA8_BYTES;
}
if (Smaa_blend_tex != 0) {
bytes += post_w * post_h * RGBA8_BYTES;
}
if (Smaa_output_tex != 0) {
bytes += post_w * post_h * RGBA8_BYTES;
}
return bytes;
}
void opengl_post_process_init()
{
// Externally used manager class to expose information about post processing externally
@@ -8,14 +8,6 @@
void opengl_post_process_init();
void opengl_post_process_shutdown();
/**
* @brief Gives the bytes that the bloom and SMAA render textures hold
*
* The profiler overlay's memory panel adds this value to the "render targets" group. The function
* counts only the textures that exist at the moment of the call.
*/
size_t opengl_get_postprocessing_render_target_bytes();
void gr_opengl_post_process_set_effect(const char *name, int x, const vec3d *rgb);
void gr_opengl_post_process_set_defaults();
void gr_opengl_post_process_save_zbuffer();
-10
View File
@@ -178,20 +178,10 @@ static SCP_string opengl_shader_get_header(shader_type type_id, int flags, bool
#ifndef USE_OPENGL_ES
sflags << "#version " << GLSL_version << " core\n";
if (GLSL_version < 330) {
sflags << "#extension GL_ARB_explicit_attrib_location : enable\n";
}
#else
sflags << "#version " << GLSL_version << " es\n";
#endif
sflags << "#define OPENGL\n";
#ifdef USE_OPENGL_ES
if (!GLAD_GL_EXT_clip_cull_distance) {
sflags << "#define SDR_FLAG_SW_CLIP_FALLBACK\n";
}
#endif
sflags << shader_get_shadow_cascade_defines();
sflags << shader_get_rt_shadow_light_limit_define();
-7
View File
@@ -360,13 +360,6 @@ GLboolean opengl_state::ClipDistance(GLint num, bool state)
GLboolean save_state = clipdistance_Status[num];
#ifdef USE_OPENGL_ES
if (!GLAD_GL_EXT_clip_cull_distance) {
clipdistance_Status[num] = state;
return save_state;
}
#endif
if (state != clipdistance_Status[num]) {
if (state) {
glEnable(GL_CLIP_DISTANCE0+num);
-108
View File
@@ -15,14 +15,11 @@
#include "gropenglstate.h"
#include "gropengltexture.h"
#include "gropengldraw.h"
#include "gropenglpostprocessing.h"
#include "gropengltnl.h"
#include "bmpman/bm_internal.h"
#include "bmpman/bmpman.h"
#include "cmdline/cmdline.h"
#include "ddsutils/ddsutils.h"
#include "globalincs/systemvars.h"
#include "graphics/2d.h"
#include "graphics/grinternal.h"
#include "math/vecmat.h"
#include "options/Option.h"
@@ -149,27 +146,6 @@ static auto AnisotropyOption = options::OptionBuilder<float>("Graphics.Anisotrop
int opengl_free_texture(tcache_slot_opengl *t);
int opengl_create_texture (int bitmap_handle, int bitmap_type, tcache_slot_opengl *tslot = NULL);
// Running total of the bytes of texture data held by all live texture cache slots, for the
// profiler overlay's memory panel. Kept in sync at every site that fills or drops a slot's data
// rather than summed on demand, because the slots live in bmpman's blocks and a full walk of them
// each frame is too expensive. Animations put all their frames in one texture array but give each
// frame its own slot, so freeing some frames of an animation lowers this total while the array
// itself stays alive until the last frame goes away.
static size_t GL_texture_bytes_used = 0;
/**
* @brief Removes a texture cache slot's bytes from GL_texture_bytes_used
*
* Call this at each site that drops the texture data of a slot, immediately before the code that
* clears the slot. The function sets the size of the slot to 0, thus a second call for the same
* slot does nothing.
*/
static void opengl_texture_release_bytes(tcache_slot_opengl* t)
{
GL_texture_bytes_used -= static_cast<size_t>(t->size);
t->size = 0;
}
extern int get_num_mipmap_levels(int w, int h);
void opengl_set_additive_tex_env()
@@ -260,14 +236,6 @@ void opengl_tcache_flush()
opengl_free_texture(static_cast<tcache_slot_opengl*>(slot.gr_info));
}
}
// A flush releases the texture of every slot, thus the total must go to 0. A value that is not
// 0 shows that a site which drops texture data does not call opengl_texture_release_bytes().
// The shutdown sequence is the one exception: bm_close() removes the blocks before it closes
// the graphics backend, thus the loop above then finds no slot to release.
Assertion(bm_blocks.empty() || (GL_texture_bytes_used == 0),
"OpenGL texture memory tracking lost " SIZE_T_ARG " bytes. A texture release site is missing.",
GL_texture_bytes_used);
}
extern void opengl_kill_all_render_targets();
@@ -366,7 +334,6 @@ int opengl_free_texture(tcache_slot_opengl *t)
// There are still used frames left in the animation so we can't free the texture object here
// We still need to reset this slot though since it has been "freed"
opengl_texture_release_bytes(t);
t->reset();
// Everything is fine
@@ -386,7 +353,6 @@ int opengl_free_texture(tcache_slot_opengl *t)
glDeleteTextures (1, &t->texture_id);
// Finally, reset the data of this slot
opengl_texture_release_bytes(t);
t->reset();
return 1;
@@ -765,14 +731,10 @@ static int opengl_texture_set_level(int bitmap_handle, int bitmap_type, int bmap
} // end switch
tSlot->bitmap_handle = bitmap_handle;
// Remove the previous bytes of the slot before the new size replaces them. This function also
// runs for a texture that is filled again (a streaming animation, for example).
opengl_texture_release_bytes(tSlot);
tSlot->size = (dsize) ? ((doffset + dsize) - skip_size) : (tex_w * tex_h * byte_mult);
tSlot->w = (ushort)tex_w;
tSlot->h = (ushort)tex_h;
GL_texture_bytes_used += static_cast<size_t>(tSlot->size);
GL_textures_in_frame += tSlot->size;
GL_CHECK_FOR_ERRORS("end of create_texture_sub()");
@@ -1002,7 +964,6 @@ int opengl_create_texture(int bitmap_handle, int bitmap_type, tcache_slot_opengl
GL_state.Texture.Delete(start_slot->texture_id);
glDeleteTextures(1, &start_slot->texture_id);
opengl_texture_release_bytes(start_slot);
start_slot->reset();
}
}
@@ -1073,7 +1034,6 @@ int opengl_create_texture(int bitmap_handle, int bitmap_type, tcache_slot_opengl
if (tslot->texture_id == 0) {
mprintf(("!!OpenGL DEBUG!! t->texture_id == 0\n"));
opengl_texture_release_bytes(tslot);
tslot->reset();
return 0;
}
@@ -1150,13 +1110,7 @@ int opengl_create_texture(int bitmap_handle, int bitmap_type, tcache_slot_opengl
auto index = frame - animation_begin;
auto frame_slot = bm_get_gr_info<tcache_slot_opengl>(frame, true);
// The copy below overwrites the size of the slot, thus the old bytes must go away first
opengl_texture_release_bytes(frame_slot);
*frame_slot = *tslot; // Copy the existing properties to the slot of this texture
// The copy also brought in the size of tslot, which belongs to a different slot and is
// already part of GL_texture_bytes_used. opengl_texture_set_level() below gives this slot
// its own size.
frame_slot->size = 0;
frame_slot->array_index = (uint32_t) index;
// set the bits per pixel
@@ -1687,7 +1641,6 @@ struct fbo_t {
// these first vars should only be modified in opengl_make_render_target()
GLuint renderbuffer_id = 0;
GLuint framebuffer_id = 0;
size_t renderbuffer_bytes = 0; // for the profiler overlay's memory panel; see GL_renderbuffer_bytes_used
int width = 0;
int height = 0;
// these next 2 should only be modifed in opengl_set_render_target()
@@ -1700,12 +1653,6 @@ static SCP_vector<fbo_t> RenderTarget;
static fbo_t *render_target = NULL;
static int next_fbo_id = 0;
// Running total of bytes used by depth/stencil renderbuffers across all live FBOs, for the
// profiler overlay's memory panel. Kept in sync with each fbo_t's renderbuffer_bytes at every
// create/delete site below rather than summed on demand, since RenderTarget entries are reused
// (opengl_get_free_fbo) and reset in place.
static size_t GL_renderbuffer_bytes_used = 0;
static fbo_t* opengl_get_fbo(int id) {
if (id < 0) {
return nullptr;
@@ -1821,31 +1768,11 @@ void opengl_kill_render_target(bitmap_slot* slot)
if (fbo->renderbuffer_id) {
glDeleteRenderbuffers(1, &fbo->renderbuffer_id);
fbo->renderbuffer_id = 0;
GL_renderbuffer_bytes_used -= fbo->renderbuffer_bytes;
fbo->renderbuffer_bytes = 0;
}
opengl_free_fbo_slot(fbo->fbo_id);
}
void gr_opengl_get_memory_stats(gr_memory_stats& stats)
{
stats.gpu_purpose_valid = true;
// The textures of the bitmap cache. Render targets that the game creates through bmpman are
// part of this group, which matches the Vulkan backend: its bm_make_render_target() images
// also go into the texture group.
stats.gpu_texture_bytes = GL_texture_bytes_used;
stats.gpu_geometry_bytes = opengl_get_geometry_bytes();
// The render target group holds the resources of the backend itself: the depth/stencil
// renderbuffers of the bitmap render targets, the scene and backbuffer textures, the
// post-processing textures, and the shadow map.
stats.gpu_render_target_bytes = GL_renderbuffer_bytes_used + opengl_get_scene_render_target_bytes() +
opengl_get_postprocessing_render_target_bytes() + opengl_get_shadow_map_bytes();
}
void opengl_kill_all_render_targets()
{
for (size_t i = 0; i < RenderTarget.size(); i++) {
@@ -1859,18 +1786,10 @@ void opengl_kill_all_render_targets()
if (fbo->renderbuffer_id) {
glDeleteRenderbuffers(1, &fbo->renderbuffer_id);
fbo->renderbuffer_id = 0;
GL_renderbuffer_bytes_used -= fbo->renderbuffer_bytes;
fbo->renderbuffer_bytes = 0;
}
}
RenderTarget.clear();
// Every renderbuffer is gone now, thus the total must go to 0. A value that is not 0 shows
// that a site which deletes a renderbuffer does not correct GL_renderbuffer_bytes_used.
Assertion(GL_renderbuffer_bytes_used == 0,
"OpenGL renderbuffer memory tracking lost " SIZE_T_ARG " bytes. A renderbuffer delete site is missing.",
GL_renderbuffer_bytes_used);
}
int opengl_set_render_target( int slot, int face, int is_static )
@@ -2027,26 +1946,6 @@ int opengl_make_render_target( int handle, int *w, int *h, int *bpp, int *mm_lvl
glTexParameteri(GL_texture_target, GL_TEXTURE_MAX_LEVEL, ts->mipmap_levels - 1);
// Record the bytes of the color texture, which opengl_tex_array_storage() gave the format
// GL_RGBA8 (4 bytes for each pixel). A cube map holds 6 faces of that size.
size_t rt_texture_bytes = 0;
for (int level = 0; level < ts->mipmap_levels; ++level) {
auto level_w = static_cast<size_t>(std::max(1, *w >> level));
auto level_h = static_cast<size_t>(std::max(1, *h >> level));
rt_texture_bytes += level_w * level_h * 4;
}
if (flags & BMP_FLAG_CUBEMAP) {
rt_texture_bytes *= 6;
}
// A slot that held a render target before must lose its old bytes first.
opengl_texture_release_bytes(ts);
// The size field of the slot is an int, thus the value must stay inside its range. The counter
// takes the same (clamped) value, which keeps the add here and the subtract in
// opengl_texture_release_bytes() equal.
ts->size = static_cast<int>(std::min(rt_texture_bytes, static_cast<size_t>(INT_MAX)));
GL_texture_bytes_used += static_cast<size_t>(ts->size);
GL_state.Texture.Enable(0);
// render buffer (depth)
@@ -2055,10 +1954,6 @@ int opengl_make_render_target( int handle, int *w, int *h, int *bpp, int *mm_lvl
glBindRenderbuffer(GL_RENDERBUFFER, new_fbo->renderbuffer_id);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, *w, *h);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// GL_DEPTH24_STENCIL8 is 4 bytes/pixel
new_fbo->renderbuffer_bytes = static_cast<size_t>(*w) * static_cast<size_t>(*h) * 4;
GL_renderbuffer_bytes_used += new_fbo->renderbuffer_bytes;
}
// frame buffer
@@ -2085,7 +1980,6 @@ int opengl_make_render_target( int handle, int *w, int *h, int *bpp, int *mm_lvl
GL_state.Texture.Delete(ts->texture_id);
glDeleteTextures(1, &ts->texture_id);
opengl_texture_release_bytes(ts);
ts->reset();
glDeleteFramebuffers(1, &new_fbo->framebuffer_id);
@@ -2093,8 +1987,6 @@ int opengl_make_render_target( int handle, int *w, int *h, int *bpp, int *mm_lvl
if (new_fbo->renderbuffer_id) {
glDeleteRenderbuffers(1, &new_fbo->renderbuffer_id);
new_fbo->renderbuffer_id = 0;
GL_renderbuffer_bytes_used -= new_fbo->renderbuffer_bytes;
new_fbo->renderbuffer_bytes = 0;
}
opengl_set_texture_target();
-2
View File
@@ -81,8 +81,6 @@ void opengl_set_additive_tex_env();
void opengl_set_modulate_tex_env();
void opengl_preload_init();
void opengl_kill_render_target(bitmap_slot* slot);
struct gr_memory_stats;
void gr_opengl_get_memory_stats(gr_memory_stats& stats);
int opengl_make_render_target(int handle, int *w, int *h, int *bpp, int *mm_lvl, int flags);
int opengl_set_render_target(int slot, int face = -1, int is_static = 0);
ubyte* gr_opengl_get_bitmap_from_texture(int bitmap_num, int* width_out, int* height_out);
+4 -65
View File
@@ -59,21 +59,7 @@ extern vec3d G3_user_clip_point;
extern bool Envmap_override;
extern bool Shadow_override;
// Running total of the bytes held by live vertex and index buffers, for the profiler overlay's
// memory panel (see opengl_get_geometry_bytes()). Uniform buffers and texture buffers stay out of
// this total, which keeps it equal to the "geometry" group of the Vulkan backend.
static size_t GL_geometry_bytes_used = 0;
// Tells if a buffer of this OpenGL type counts as geometry for GL_geometry_bytes_used.
static bool opengl_buffer_is_geometry(GLenum type)
{
return (type == GL_ARRAY_BUFFER) || (type == GL_ELEMENT_ARRAY_BUFFER);
}
size_t opengl_get_geometry_bytes()
{
return GL_geometry_bytes_used;
}
size_t GL_vertex_data_in = 0;
GLint GL_max_elements_vertices = 4096;
GLint GL_max_elements_indices = 4096;
@@ -82,11 +68,6 @@ GLuint Shadow_map_depth_texture = 0;
GLuint shadow_fbo = 0;
int Shadow_texture_size = 0;
// Number of layers in the shadow map texture array, kept from the moment
// opengl_init_shadow_framebuffer() created the array. opengl_get_shadow_map_bytes() uses it, thus
// a later change of the cascade counts cannot make that value wrong.
static int Shadow_texture_layers = 0;
gr_buffer_handle Transform_buffer_handle;
SCP_unordered_map<vertex_layout, GLuint> Stored_vertex_arrays;
@@ -294,11 +275,9 @@ void gr_opengl_update_buffer_data(gr_buffer_handle handle, size_t size, const vo
glBufferData(buffer_obj.type, size, data, buffer_obj.gl_usage);
}
if (opengl_buffer_is_geometry(buffer_obj.type)) {
GL_geometry_bytes_used -= buffer_obj.size;
GL_geometry_bytes_used += size;
}
GL_vertex_data_in -= buffer_obj.size;
buffer_obj.size = size;
GL_vertex_data_in += buffer_obj.size;
}
void gr_opengl_update_buffer_data_offset(gr_buffer_handle handle, size_t offset, size_t size, const void* data)
@@ -386,12 +365,7 @@ void gr_opengl_delete_buffer(gr_buffer_handle handle)
glDeleteTextures(1, &buffer_obj.texture);
}
if (opengl_buffer_is_geometry(buffer_obj.type)) {
GL_geometry_bytes_used -= buffer_obj.size;
}
// The size must go to 0 here. opengl_destroy_all_buffers() calls this function for every
// handle, thus a buffer that the game already deleted comes through a second time.
buffer_obj.size = 0;
GL_vertex_data_in -= buffer_obj.size;
glDeleteBuffers(1, &buffer_obj.buffer_id);
}
@@ -513,7 +487,6 @@ static bool opengl_init_shadow_framebuffer(int size)
// Everything is fine
mprintf(("Shadow framebuffer created successfully.\n"));
Shadow_texture_size = size;
Shadow_texture_layers = Num_shadow_cascades + Num_cockpit_shadow_cascades;
return true;
}
@@ -544,22 +517,6 @@ static bool opengl_init_shadow_framebuffer(int size)
return false;
}
size_t opengl_get_shadow_map_bytes()
{
if (Shadow_map_depth_texture == 0) {
return 0;
}
// opengl_init_shadow_framebuffer() gives the texture array the internal format
// GL_DEPTH_COMPONENT24, which the driver holds in 4 bytes for each texel.
constexpr size_t DEPTH_COMPONENT24_BYTES = 4;
const auto size = static_cast<size_t>(Shadow_texture_size);
const auto layers = static_cast<size_t>(Shadow_texture_layers);
return size * size * layers * DEPTH_COMPONENT24_BYTES;
}
void opengl_tnl_init()
{
Transform_buffer_handle = opengl_create_texture_buffer_object();
@@ -775,13 +732,7 @@ void gr_opengl_shadow_map_start(matrix4 *shadow_view_matrix, const matrix *light
glClear(GL_DEPTH_BUFFER_BIT);
#ifdef USE_OPENGL_ES
if (GLAD_GL_EXT_depth_clamp) {
glEnable(GL_DEPTH_CLAMP);
}
#else
glEnable(GL_DEPTH_CLAMP);
#endif
Glowpoint_override_save = Glowpoint_override;
Glowpoint_override = true;
@@ -802,13 +753,7 @@ void gr_opengl_shadow_map_end()
{
gr_end_view_matrix();
#ifdef USE_OPENGL_ES
if (GLAD_GL_EXT_depth_clamp) {
glDisable(GL_DEPTH_CLAMP);
}
#else
glDisable(GL_DEPTH_CLAMP);
#endif
gr_zbuffer_set(ZBUFFER_TYPE_FULL);
GL_state.PopFramebufferState();
@@ -1191,12 +1136,6 @@ void opengl_tnl_set_material_nanovg(nanovg_material* material_info) {
void opengl_tnl_set_material_decal(decal_material* material_info) {
opengl_tnl_set_material(material_info, false);
#ifdef USE_OPENGL_ES
// ES decal outputs use G-buffer locations instead of packed draw-buffer indices.
GL_state.SetAlphaBlendModei(2, material_info->get_blend_mode(1));
GL_state.SetAlphaBlendModei(4, material_info->get_blend_mode(2));
#endif
float u_scale, v_scale;
uint32_t array_index;
-16
View File
@@ -53,22 +53,6 @@ void gr_opengl_bind_uniform_buffer(uniform_block_type bind_point, size_t offset,
void* gr_opengl_map_buffer(gr_buffer_handle handle);
void gr_opengl_flush_mapped_buffer(gr_buffer_handle handle, size_t offset, size_t size);
/**
* @brief Gives the bytes that all live vertex and index buffers hold
*
* The profiler overlay's memory panel shows this value as the "geometry" group. Uniform buffers
* and texture buffers are not part of it.
*/
size_t opengl_get_geometry_bytes();
/**
* @brief Gives the bytes that the shadow map texture array holds
*
* The profiler overlay's memory panel adds this value to the "render targets" group. The function
* gives 0 when shadows are off.
*/
size_t opengl_get_shadow_map_bytes();
/**
* @brief Retrieves the OpenGL handle of a generic buffer handle
* @param expected_type The expected type of the buffer. Mainly used for debug checking.
-13
View File
@@ -162,19 +162,6 @@ static ShaderVariantInfo SHADER_VARIANTS[] = {
{SDR_TYPE_SHADOW_MAP_GEN, true, SDR_FLAG_SHADOW_FALLBACK, "GEOMETRY_FALLBACK", {}, "Use a geometry shader to select the cascade layer instead of writing gl_Layer from the vertex shader"}
};
static_assert(VATTRIB_MODEL_MATRIX + 4 <= 32, "vertex input mask is 32 bits wide");
uint32_t shader_vertex_input_mask(const ShaderTypeInfo& info)
{
uint32_t mask = 0;
for (auto attr : info.attributes) {
// The Vulkan pipeline drops every location the mask omits, so a mat4 needs all four of its bits.
const uint32_t locations = (attr == VATTRIB_MODEL_MATRIX) ? 4u : 1u;
mask |= ((1u << locations) - 1u) << attr;
}
return mask;
}
const ShaderTypeInfo* shader_get_type_info(shader_type type)
{
for (auto & i : SHADER_TYPES) {
-8
View File
@@ -55,14 +55,6 @@ const ShaderTypeInfo* shader_get_type_info(shader_type type);
// Lookup helper: find variant info by (type, flag). Returns nullptr if not found.
const ShaderVariantInfo* shader_get_variant_info(shader_type type, int flag);
/**
* @brief Bitmask of the vertex attribute locations a shader type reads.
*
* One bit per location; VATTRIB_MODEL_MATRIX contributes bits 8-11 because a
* mat4 input occupies four consecutive locations.
*/
uint32_t shader_vertex_input_mask(const ShaderTypeInfo& info);
// Iterate all variants matching type whose flag bit is set in flags.
void shader_for_each_active_variant(shader_type type, unsigned int flags, const std::function<void(const ShaderVariantInfo&)>& fn);
-6
View File
@@ -70,12 +70,6 @@ void GPUMemoryHeap::freeGpuData(size_t offset) {
gr_buffer_handle GPUMemoryHeap::bufferHandle() {
return _bufferHandle;
}
size_t GPUMemoryHeap::usedBytes() const {
return _allocator->usedBytes();
}
size_t GPUMemoryHeap::bufferSize() const {
return _bufferSize;
}
}
}
-12
View File
@@ -54,18 +54,6 @@ class GPUMemoryHeap {
* @return The graphics code buffer handle.
*/
gr_buffer_handle bufferHandle();
/**
* @brief Gets the number of bytes currently allocated from this heap
* @return The sum of the sizes of all active allocations.
*/
size_t usedBytes() const;
/**
* @brief Gets the total size of the backing GPU buffer, including both allocated and free space
* @return The current buffer size.
*/
size_t bufferSize() const;
};
}
+2 -18
View File
@@ -339,22 +339,7 @@ vk::BufferUsageFlags VulkanBufferManager::getVkUsageFlags(BufferType type, bool
return flags;
}
namespace {
MemoryPurpose bufferTypeToPurpose(BufferType type)
{
switch (type) {
case BufferType::Vertex:
case BufferType::Index:
return MemoryPurpose::Geometry;
case BufferType::Uniform:
default:
// Uniform buffers are already tracked separately via UniformBufferManager/gr_debug_stats.
return MemoryPurpose::Unknown;
}
}
}
MemoryUsage VulkanBufferManager::getMemoryUsage(BufferUsageHint hint)
MemoryUsage VulkanBufferManager::getMemoryUsage(BufferUsageHint hint)
{
switch (hint) {
case BufferUsageHint::Static:
@@ -470,8 +455,7 @@ bool VulkanBufferManager::createOrResizeBuffer(VulkanBufferObject& bufferObj, si
// Allocate memory
MemoryUsage memUsage = getMemoryUsage(bufferObj.usage);
if (!m_memoryManager->allocateBufferMemory(
bufferObj.buffer, memUsage, bufferObj.allocation, bufferTypeToPurpose(bufferObj.type))) {
if (!m_memoryManager->allocateBufferMemory(bufferObj.buffer, memUsage, bufferObj.allocation)) {
m_device.destroyBuffer(bufferObj.buffer);
bufferObj.buffer = oldBuffer;
bufferObj.allocation = oldAllocation;
-14
View File
@@ -132,20 +132,6 @@ vk::PrimitiveTopology convertPrimitiveType(primitive_type type)
}
}
bool topologySupportsPrimitiveRestart(vk::PrimitiveTopology topology)
{
switch (topology) {
case vk::PrimitiveTopology::eLineStrip:
case vk::PrimitiveTopology::eTriangleStrip:
case vk::PrimitiveTopology::eTriangleFan:
case vk::PrimitiveTopology::eLineStripWithAdjacency:
case vk::PrimitiveTopology::eTriangleStripWithAdjacency:
return true;
default:
return false;
}
}
vk::CullModeFlags convertCullMode(bool cullEnabled)
{
return cullEnabled ? vk::CullModeFlagBits::eBack : vk::CullModeFlagBits::eNone;
-9
View File
@@ -46,15 +46,6 @@ vk::StencilOp convertStencilOp(StencilOperation op);
*/
vk::PrimitiveTopology convertPrimitiveType(primitive_type type);
/**
* @brief Whether a topology may declare primitiveRestartEnable
*
* True for the strip/fan topologies. For list topologies the Vulkan spec requires
* primitiveRestartEnable to be VK_FALSE unless VK_EXT_primitive_topology_list_restart is
* enabled, which FSO does not request.
*/
bool topologySupportsPrimitiveRestart(vk::PrimitiveTopology topology);
/**
* @brief Convert FSO cull mode to Vulkan cull mode
* @param cullEnabled Whether culling is enabled
+18 -16
View File
@@ -374,13 +374,13 @@ void vulkan_deferred_lighting_msaa()
// Global set (fallback — resolve shader doesn't use global bindings)
vk::DescriptorSet globalSet = descriptorMgr->allocateFrameSet(DescriptorSetIndex::Global);
Assert(globalSet);
writer.writeSet(DescriptorSetIndex::Global, globalSet);
writer.writeSet(globalSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::Global));
// Material set: All 6 MSAA textures in binding 1 array (elements 0-5)
// [0]=color, [1]=position, [2]=normal, [3]=specular, [4]=emissive, [5]=depth
vk::DescriptorSet materialSet = descriptorMgr->allocateFrameSet(DescriptorSetIndex::Material);
Assert(materialSet);
writer.writeSet(DescriptorSetIndex::Material, materialSet);
writer.writeSet(materialSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::Material));
// Build texture array: elements 0-5 are MSAA textures, 6-15 are fallback
vk::Sampler nearestSampler = texMgr->getSampler(
@@ -402,7 +402,7 @@ void vulkan_deferred_lighting_msaa()
// PerDraw set: GenericData UBO with {samples, fov} at binding 0
vk::DescriptorSet perDrawSet = descriptorMgr->allocateFrameSet(DescriptorSetIndex::PerDraw);
Assert(perDrawSet);
writer.writeSet(DescriptorSetIndex::PerDraw, perDrawSet);
writer.writeSet(perDrawSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::PerDraw));
struct MsaaResolveData {
int samples;
@@ -416,8 +416,15 @@ void vulkan_deferred_lighting_msaa()
ring.alloc(descriptorMgr->getCurrentFrame(), &resolveData, sizeof(resolveData));
writer.setBuffer(PerDrawBinding::GenericData, {ring.buffer(), slotOffset, ring.slotSize()});
writer.flush();
const vk::DescriptorSet sets[] = {globalSet, materialSet, perDrawSet};
writer.bindSets(cmd, pipelineMgr->getPipelineLayout(), DescriptorSetIndex::Global, sets);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
pipelineMgr->getPipelineLayout(),
static_cast<uint32_t>(DescriptorSetIndex::Global), globalSet, {});
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
pipelineMgr->getPipelineLayout(),
static_cast<uint32_t>(DescriptorSetIndex::Material), materialSet, {});
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
pipelineMgr->getPipelineLayout(),
static_cast<uint32_t>(DescriptorSetIndex::PerDraw), perDrawSet, {});
cmd.draw(3, 1, 0, 0);
}
@@ -952,7 +959,6 @@ void vulkan_render_decals(decal_material* material_info,
// Build pipeline config for decal rendering
PipelineConfig config;
config.shaderType = SDR_TYPE_DECAL;
config.shaderFlags = material_info->get_shader_flags();
config.primitiveType = prim_type;
config.depthMode = material_info->get_depth_mode();
config.depthWriteEnabled = false;
@@ -1007,12 +1013,12 @@ void vulkan_render_decals(decal_material* material_info,
// Set 0: Global (all fallback)
vk::DescriptorSet globalSet = descManager->allocateFrameSet(DescriptorSetIndex::Global);
Assert(globalSet);
writer.writeSet(DescriptorSetIndex::Global, globalSet);
writer.writeSet(globalSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::Global));
// Set 1: Material
vk::DescriptorSet materialSet = descManager->allocateFrameSet(DescriptorSetIndex::Material);
Assert(materialSet);
writer.writeSet(DescriptorSetIndex::Material, materialSet);
writer.writeSet(materialSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::Material));
// Binding 2: DecalGlobals UBO
writer.setBuffer(MaterialBinding::DecalGlobals,
@@ -1046,19 +1052,15 @@ void vulkan_render_decals(decal_material* material_info,
// Set 2: PerDraw
vk::DescriptorSet perDrawSet = descManager->allocateFrameSet(DescriptorSetIndex::PerDraw);
Assert(perDrawSet);
writer.writeSet(DescriptorSetIndex::PerDraw, perDrawSet);
writer.writeSet(perDrawSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::PerDraw));
writer.setBuffer(PerDrawBinding::Matrices,
getPendingBufInfo(static_cast<size_t>(uniform_block_type::Matrices)));
writer.setBuffer(PerDrawBinding::DecalInfo,
getPendingBufInfo(static_cast<size_t>(uniform_block_type::DecalInfo)));
writer.flush();
stateTracker->bindDescriptorSet(DescriptorSetIndex::Global, globalSet, nullptr);
stateTracker->bindDescriptorSet(DescriptorSetIndex::Material,
materialSet,
writer.dynamicOffsets(DescriptorSetIndex::Material));
stateTracker->bindDescriptorSet(DescriptorSetIndex::PerDraw,
perDrawSet,
writer.dynamicOffsets(DescriptorSetIndex::PerDraw));
stateTracker->bindDescriptorSet(DescriptorSetIndex::Global, globalSet);
stateTracker->bindDescriptorSet(DescriptorSetIndex::Material, materialSet);
stateTracker->bindDescriptorSet(DescriptorSetIndex::PerDraw, perDrawSet);
// Bind vertex buffers: binding 0 = box VBO, binding 1 = instance buffer
vk::Buffer boxVBO = bufferManager->getVkBuffer(buffers.Vbuffer_handle);
+42 -119
View File
@@ -19,12 +19,6 @@ static constexpr DescriptorBindingTemplate s_globalBindings[] = {
static constexpr DescriptorBindingTemplate s_globalTlasBinding{
GlobalBinding::Tlas, vk::DescriptorType::eAccelerationStructureKHR, 1, vk::ShaderStageFlagBits::eFragment};
// GLOBAL_DYNAMIC_OFFSET_COUNT is stated rather than derived because this set's template is
// assembled at runtime (see below); keep the two in agreement. The optional TLAS binding is not
// a dynamic UBO, so it cannot change the count either.
static_assert(dynamic_offset_count(s_globalBindings) == GLOBAL_DYNAMIC_OFFSET_COUNT,
"The Global set gained a dynamic binding; GLOBAL_DYNAMIC_OFFSET_COUNT must follow.");
// The Global set's layout must omit the TLAS binding entirely on devices without
// VK_KHR_acceleration_structure enabled -- a descriptor type the device didn't
// enable the extension for is invalid in vkCreateDescriptorSetLayout. So, unlike
@@ -33,10 +27,26 @@ static_assert(dynamic_offset_count(s_globalBindings) == GLOBAL_DYNAMIC_OFFSET_CO
// the per-instance m_globalBindingsRuntime/m_globalTemplateRuntime members
// (see VulkanDescriptorManager.h), rather than compiled in as a fixed constexpr array.
// The binding declarations themselves live in the header (MaterialSetBindings /
// PerDrawSetBindings) because the dynamic-offset layout is derived from them there.
static constexpr DescriptorSetTemplate s_materialTemplate(MaterialSetBindings);
static constexpr DescriptorSetTemplate s_perDrawTemplate(PerDrawSetBindings);
static constexpr DescriptorBindingTemplate s_materialBindings[] = {
{MaterialBinding::ModelData, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{MaterialBinding::TextureArray, vk::DescriptorType::eCombinedImageSampler, 16, vk::ShaderStageFlagBits::eFragment, vk::ImageViewType::e2DArray},
{MaterialBinding::DecalGlobals, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{MaterialBinding::TransformSSBO, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eVertex},
{MaterialBinding::DepthMap, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
{MaterialBinding::SceneColor, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
{MaterialBinding::DistortionMap, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
{MaterialBinding::ShadowMapData, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex},
};
static constexpr DescriptorSetTemplate s_materialTemplate(s_materialBindings);
static constexpr DescriptorBindingTemplate s_perDrawBindings[] = {
{PerDrawBinding::GenericData, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{PerDrawBinding::Matrices, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{PerDrawBinding::NanoVGData, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{PerDrawBinding::DecalInfo, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{PerDrawBinding::MovieData, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eFragment},
};
static constexpr DescriptorSetTemplate s_perDrawTemplate(s_perDrawBindings);
// ========== Static uniform binding mappings ==========
@@ -82,21 +92,12 @@ const vk::DescriptorImageInfo& DescriptorFallbacks::getImage(vk::ImageViewType t
// ========== DescriptorWriter template-based methods ==========
void DescriptorWriter::writeSet(DescriptorSetIndex setIndex, vk::DescriptorSet set)
void DescriptorWriter::writeSet(vk::DescriptorSet set, const DescriptorSetTemplate& tmpl)
{
Assert(m_fallbacks);
const DescriptorSetTemplate& tmpl = VulkanDescriptorManager::getSetTemplate(setIndex);
// Clear binding slots for this set
m_bindingSlots = {};
m_currentSet = setIndex;
// Dynamic offsets are consumed in binding order, so assign slots as the template is walked
// (templates list bindings in ascending binding number). This must stay the same rule
// dynamic_offset_slot() applies — hence advancing by b.count, not by one binding.
uint32_t nextDynIndex = 0;
m_dynOffsets[static_cast<size_t>(setIndex)] = {};
for (const auto& b : tmpl) {
Assert(m_writeCount < MAX_WRITES);
@@ -115,11 +116,6 @@ void DescriptorWriter::writeSet(DescriptorSetIndex setIndex, vk::DescriptorSet s
bool isImage = (b.type == vk::DescriptorType::eCombinedImageSampler);
bool isAccelStruct = (b.type == vk::DescriptorType::eAccelerationStructureKHR);
if (b.type == vk::DescriptorType::eUniformBufferDynamic) {
Assert(nextDynIndex + b.count <= MAX_DYNAMIC_OFFSETS_PER_SET);
slot.dynIndex = static_cast<int>(nextDynIndex);
nextDynIndex += b.count;
}
if (isImage) {
Assert(m_imageInfoCount + b.count <= MAX_IMAGE_INFOS);
auto* dst = &m_imageInfos[m_imageInfoCount];
@@ -148,40 +144,12 @@ void DescriptorWriter::writeSet(DescriptorSetIndex setIndex, vk::DescriptorSet s
} else {
Assert(m_bufferInfoCount < MAX_BUFFER_INFOS);
m_bufferInfos[m_bufferInfoCount] = m_fallbacks->buffer;
// The fallback buffer info starts at offset 0, which is also the base a
// dynamic descriptor needs (its offset lives in the dynamic-offset array,
// left at 0 here until setBuffer supplies a real one).
w.pBufferInfo = &m_bufferInfos[m_bufferInfoCount];
slot.bufferInfo = &m_bufferInfos[m_bufferInfoCount++];
}
}
}
void DescriptorWriter::bindSets(vk::CommandBuffer cmd,
vk::PipelineLayout layout,
DescriptorSetIndex firstSet,
ArrayView<vk::DescriptorSet> sets)
{
size_t out = 0;
for (size_t i = 0; i < sets.size; ++i) {
const auto set = static_cast<DescriptorSetIndex>(static_cast<uint32_t>(firstSet) + i);
Assert(static_cast<uint32_t>(set) < static_cast<uint32_t>(DescriptorSetIndex::Count));
const uint32_t dynCount = VulkanDescriptorManager::getDynamicOffsetCount(set);
Assert(dynCount <= MAX_DYNAMIC_OFFSETS_PER_SET);
for (uint32_t j = 0; j < dynCount; ++j) {
Assert(out < m_dynOffsetScratch.size());
m_dynOffsetScratch[out++] = m_dynOffsets[static_cast<size_t>(set)][j];
}
}
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
layout,
static_cast<uint32_t>(firstSet),
vk::ArrayProxy<const vk::DescriptorSet>(static_cast<uint32_t>(sets.size), sets.data),
vk::ArrayProxy<const uint32_t>(static_cast<uint32_t>(out), m_dynOffsetScratch.data()));
}
void DescriptorWriter::flush()
{
if (m_writeCount > 0) {
@@ -199,25 +167,6 @@ void DescriptorWriter::setBuffer(uint32_t binding, const vk::DescriptorBufferInf
Assert(binding < MAX_BINDINGS_PER_SET);
auto& slot = m_bindingSlots[binding];
Assert(slot.bufferInfo);
if (slot.dynIndex >= 0) {
// Dynamic binding: the descriptor holds only {buffer, 0, range}; the caller's
// offset becomes the dynamic offset applied at bind time. Keeping it out of
// the descriptor is what lets the set survive an offset-only change.
auto& dyn = m_dynOffsets[static_cast<size_t>(m_currentSet)][static_cast<size_t>(slot.dynIndex)];
if (info.buffer) {
Assertion(info.offset <= static_cast<vk::DeviceSize>(UINT32_MAX),
"Dynamic uniform buffer offset " SIZE_T_ARG " does not fit in a uint32 dynamic offset!",
static_cast<size_t>(info.offset));
*slot.bufferInfo = vk::DescriptorBufferInfo(info.buffer, 0, info.range);
dyn = static_cast<uint32_t>(info.offset);
} else {
*slot.bufferInfo = m_fallbacks->buffer;
dyn = 0;
}
return;
}
if (info.buffer) {
*slot.bufferInfo = info;
} else {
@@ -330,23 +279,6 @@ const DescriptorSetTemplate& VulkanDescriptorManager::getSetTemplate(DescriptorS
}
}
uint32_t VulkanDescriptorManager::getDynamicOffsetCount(DescriptorSetIndex setIndex)
{
// Constant per set (see the derived *_DYNAMIC_OFFSET_COUNT values), not a scan of the
// template — this runs on every descriptor set bind.
switch (setIndex) {
case DescriptorSetIndex::Global:
return GLOBAL_DYNAMIC_OFFSET_COUNT;
case DescriptorSetIndex::Material:
return MATERIAL_DYNAMIC_OFFSET_COUNT;
case DescriptorSetIndex::PerDraw:
return PERDRAW_DYNAMIC_OFFSET_COUNT;
default:
UNREACHABLE("Invalid descriptor set index %u!", static_cast<uint32_t>(setIndex));
return 0;
}
}
vk::DescriptorSetLayout VulkanDescriptorManager::getSetLayout(DescriptorSetIndex setIndex) const
{
return m_setLayouts[static_cast<size_t>(setIndex)].get();
@@ -362,31 +294,22 @@ vk::DescriptorSet VulkanDescriptorManager::allocateFrameSet(DescriptorSetIndex s
auto& pools = m_framePools[m_currentFrame];
++m_setsAllocatedThisFrame;
// Allocate through the non-throwing, non-allocating vulkan.hpp overload: this is
// one of the hottest calls in the backend (thousands per frame), and the
// vector-returning overload heap-allocates on every single one.
vk::DescriptorSetAllocateInfo allocInfo;
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &layout;
// Try allocating from the last pool in the list
if (!pools.empty()) {
vk::DescriptorSetAllocateInfo allocInfo;
allocInfo.descriptorPool = pools.back().get();
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &layout;
vk::DescriptorSet set;
vk::Result result = m_device.allocateDescriptorSets(&allocInfo, &set);
if (result == vk::Result::eSuccess) {
return set;
}
if (result == vk::Result::eErrorOutOfPoolMemory) {
try {
auto sets = m_device.allocateDescriptorSets(allocInfo);
return sets[0];
} catch (const vk::OutOfPoolMemoryError&) {
// Pool exhausted, fall through to create a new one
nprintf(("vulkan", "VulkanDescriptorManager: frame pool exhausted, allocating an additional pool chunk\n"));
} else if (result == vk::Result::eErrorFragmentedPool) {
} catch (const vk::FragmentedPoolError&) {
// Pool fragmented, fall through to create a new one
nprintf(("vulkan", "VulkanDescriptorManager: frame pool fragmented, allocating an additional pool chunk\n"));
} else {
nprintf(("vulkan",
"VulkanDescriptorManager: descriptor set allocation failed (%s)\n",
vk::to_string(result).c_str()));
return {};
}
}
@@ -395,17 +318,18 @@ vk::DescriptorSet VulkanDescriptorManager::allocateFrameSet(DescriptorSetIndex s
nprintf(("vulkandescriptor", "VulkanDescriptorManager: Grew frame %u pool count to %zu\n",
m_currentFrame, pools.size()));
vk::DescriptorSetAllocateInfo allocInfo;
allocInfo.descriptorPool = pools.back().get();
allocInfo.descriptorSetCount = 1;
allocInfo.pSetLayouts = &layout;
vk::DescriptorSet set;
vk::Result result = m_device.allocateDescriptorSets(&allocInfo, &set);
if (result != vk::Result::eSuccess) {
nprintf(("vulkan",
"VulkanDescriptorManager: Failed to allocate frame descriptor set after pool growth: %s\n",
vk::to_string(result).c_str()));
try {
auto sets = m_device.allocateDescriptorSets(allocInfo);
return sets[0];
} catch (const vk::SystemError& e) {
nprintf(("vulkan", "VulkanDescriptorManager: Failed to allocate frame descriptor set after pool growth: %s\n", e.what()));
return {};
}
return set;
}
void VulkanDescriptorManager::beginFrame()
@@ -479,10 +403,9 @@ vk::UniqueDescriptorPool VulkanDescriptorManager::createFramePool()
// constants in VulkanDescriptorManager.h. Additional pool chunks are created
// automatically when one is exhausted.
SCP_vector<vk::DescriptorPoolSize> poolSizes = {
{vk::DescriptorType::eUniformBuffer, MAX_UNIFORM_BUFFERS_PER_POOL},
{vk::DescriptorType::eUniformBufferDynamic, MAX_DYNAMIC_UNIFORM_BUFFERS_PER_POOL},
{vk::DescriptorType::eCombinedImageSampler, MAX_SAMPLERS_PER_POOL},
{vk::DescriptorType::eStorageBuffer, MAX_SETS_PER_POOL},
{ vk::DescriptorType::eUniformBuffer, MAX_UNIFORM_BUFFERS_PER_POOL },
{ vk::DescriptorType::eCombinedImageSampler, MAX_SAMPLERS_PER_POOL },
{ vk::DescriptorType::eStorageBuffer, MAX_SETS_PER_POOL },
};
if (m_raytracingEnabled) {
poolSizes.emplace_back(vk::DescriptorType::eAccelerationStructureKHR, MAX_SETS_PER_POOL);
+79 -279
View File
@@ -52,6 +52,71 @@ struct DescriptorFallbacks {
const vk::DescriptorImageInfo& getImage(vk::ImageViewType t) const;
};
/**
* @brief Stack-allocated batch writer for descriptor set updates.
*
* Usage: reset() + writeSet() (pre-fills all bindings with fallbacks)
* + setBuffer/setImage overrides for real data + flush().
*/
class DescriptorWriter {
public:
// Fixed capacities for the stack-allocated staging arrays below — NOT Vulkan
// hardware limits. They bound a single vkUpdateDescriptorSets batch (one
// writeSet), sized to comfortably cover the largest descriptor set layout the
// engine builds. Because the backing arrays live on the stack (see m_writes
// etc.), the numbers carry no runtime cost beyond that fixed footprint; the
// only failure mode is under-sizing, which is Assert-guarded at fill time in
// DescriptorWriter::writeSet (e.g. `Assert(m_writeCount < MAX_WRITES)`) so an
// oversized layout trips loudly in debug rather than silently overflowing.
// Raise these if a future set layout legitimately needs more bindings/infos.
static constexpr uint32_t MAX_WRITES = 32; // distinct bindings written per batch
static constexpr uint32_t MAX_BUFFER_INFOS = 20; // buffer descriptors staged per batch
static constexpr uint32_t MAX_IMAGE_INFOS = 24; // image/sampler descriptors staged per batch
static constexpr uint32_t MAX_ACCEL_STRUCT_INFOS = 2; // TLAS descriptors staged per batch (RT)
static constexpr uint32_t MAX_BINDINGS_PER_SET = 16; // highest binding number addressable in a set
void reset(vk::Device device, const DescriptorFallbacks& fallbacks) {
m_device = device;
m_fallbacks = &fallbacks;
m_writeCount = 0;
m_bufferInfoCount = 0;
m_imageInfoCount = 0;
m_accelStructInfoCount = 0;
}
void writeSet(vk::DescriptorSet set, const DescriptorSetTemplate& tmpl);
void setBuffer(uint32_t binding, const vk::DescriptorBufferInfo& info);
void setImage(uint32_t binding, const vk::DescriptorImageInfo& info);
void setImageArray(uint32_t binding, ArrayView<vk::DescriptorImageInfo> infos);
void flush(); // defined in VulkanDescriptorManager.cpp (reports write stats to the manager)
private:
// Per-binding lookup for the current writeSet, indexed by binding number.
// Populated by writeSet, used by setBuffer/setImage/setImageArray for O(1) access.
struct BindingSlot {
vk::DescriptorBufferInfo* bufferInfo = nullptr; // non-null for buffer bindings
vk::DescriptorImageInfo* imageInfo = nullptr; // non-null for image bindings
uint32_t count = 0; // descriptor count (1 or 16 for arrays)
vk::ImageViewType viewType = vk::ImageViewType::e2D; // for fallback lookup
};
vk::Device m_device;
const DescriptorFallbacks* m_fallbacks = nullptr;
std::array<vk::WriteDescriptorSet, MAX_WRITES> m_writes;
std::array<vk::DescriptorBufferInfo, MAX_BUFFER_INFOS> m_bufferInfos;
std::array<vk::DescriptorImageInfo, MAX_IMAGE_INFOS> m_imageInfos;
std::array<vk::AccelerationStructureKHR, MAX_ACCEL_STRUCT_INFOS> m_accelStructInfos;
std::array<vk::WriteDescriptorSetAccelerationStructureKHR, MAX_ACCEL_STRUCT_INFOS> m_asWriteInfos;
std::array<BindingSlot, MAX_BINDINGS_PER_SET> m_bindingSlots;
uint32_t m_writeCount = 0;
uint32_t m_bufferInfoCount = 0;
uint32_t m_imageInfoCount = 0;
uint32_t m_accelStructInfoCount = 0;
};
/**
* @brief Descriptor set indices for the 3-tier layout
*
@@ -81,18 +146,15 @@ namespace GlobalBinding {
}
// Material Set (Set 1) bindings — per-material data
// ModelData and ShadowMapData are *dynamic* UBOs: their offset moves every draw
// while the rest of the set is unchanged, so keeping the offset out of the
// descriptor is what makes the set cacheable across draws.
namespace MaterialBinding {
static constexpr uint32_t ModelData = 0; // dynamic UBO: model/material data
static constexpr uint32_t TextureArray = 1; // sampler2D[16]: material textures
static constexpr uint32_t DecalGlobals = 2; // UBO: decal globals
static constexpr uint32_t TransformSSBO = 3; // SSBO: batched transforms
static constexpr uint32_t DepthMap = 4; // sampler2D: depth (soft particles)
static constexpr uint32_t SceneColor = 5; // sampler2D: scene color (distortion)
static constexpr uint32_t DistortionMap = 6; // sampler2D: distortion texture
static constexpr uint32_t ShadowMapData = 7; // dynamic UBO: shadow map generation per-draw data (shadow_render_list)
static constexpr uint32_t ModelData = 0; // UBO: model/material data
static constexpr uint32_t TextureArray = 1; // sampler2D[16]: material textures
static constexpr uint32_t DecalGlobals = 2; // UBO: decal globals
static constexpr uint32_t TransformSSBO = 3; // SSBO: batched transforms
static constexpr uint32_t DepthMap = 4; // sampler2D: depth (soft particles)
static constexpr uint32_t SceneColor = 5; // sampler2D: scene color (distortion)
static constexpr uint32_t DistortionMap = 6; // sampler2D: distortion texture
static constexpr uint32_t ShadowMapData = 7; // UBO: shadow map generation per-draw data (shadow_render_list)
}
// Texture array slot indices (elements within MaterialBinding::TextureArray)
@@ -107,256 +169,14 @@ namespace TextureSlot {
}
// PerDraw Set (Set 2) bindings — per-draw-call data
// GenericData and Matrices are *dynamic* UBOs, for the same reason as the
// Material set's ModelData.
namespace PerDrawBinding {
static constexpr uint32_t GenericData = 0; // dynamic UBO: generic shader data
static constexpr uint32_t Matrices = 1; // dynamic UBO: transform matrices
static constexpr uint32_t NanoVGData = 2; // UBO: NanoVG data
static constexpr uint32_t DecalInfo = 3; // UBO: per-decal info
static constexpr uint32_t MovieData = 4; // UBO: movie playback data
static constexpr uint32_t GenericData = 0; // UBO: generic shader data
static constexpr uint32_t Matrices = 1; // UBO: transform matrices
static constexpr uint32_t NanoVGData = 2; // UBO: NanoVG data
static constexpr uint32_t DecalInfo = 3; // UBO: per-decal info
static constexpr uint32_t MovieData = 4; // UBO: movie playback data
}
// ========== Set layout templates ==========
//
// These describe the fixed set layouts. Everything downstream that needs to know
// which bindings are dynamic, how many there are, or which dynamic-offset slot a
// given binding occupies is derived from them below rather than restated, so the
// declarations here are the only place that ordering exists.
inline constexpr DescriptorBindingTemplate MaterialSetBindings[] = {
{MaterialBinding::ModelData,
vk::DescriptorType::eUniformBufferDynamic,
1,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{MaterialBinding::TextureArray,
vk::DescriptorType::eCombinedImageSampler,
16,
vk::ShaderStageFlagBits::eFragment,
vk::ImageViewType::e2DArray},
{MaterialBinding::DecalGlobals,
vk::DescriptorType::eUniformBuffer,
1,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{MaterialBinding::TransformSSBO, vk::DescriptorType::eStorageBuffer, 1, vk::ShaderStageFlagBits::eVertex},
{MaterialBinding::DepthMap, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
{MaterialBinding::SceneColor, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
{MaterialBinding::DistortionMap, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
{MaterialBinding::ShadowMapData, vk::DescriptorType::eUniformBufferDynamic, 1, vk::ShaderStageFlagBits::eVertex},
};
inline constexpr DescriptorBindingTemplate PerDrawSetBindings[] = {
{PerDrawBinding::GenericData,
vk::DescriptorType::eUniformBufferDynamic,
1,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{PerDrawBinding::Matrices,
vk::DescriptorType::eUniformBufferDynamic,
1,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{PerDrawBinding::NanoVGData,
vk::DescriptorType::eUniformBuffer,
1,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{PerDrawBinding::DecalInfo,
vk::DescriptorType::eUniformBuffer,
1,
vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment},
{PerDrawBinding::MovieData, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eFragment},
};
// ========== Dynamic-offset layout, derived from the templates above ==========
//
// vkCmdBindDescriptorSets consumes a set's dynamic offsets in ascending binding
// order, one entry per eUniformBufferDynamic descriptor. Both the count and the
// slot each binding occupies are computed from the set templates so a call site
// can never disagree with the layout it is binding against — reordering a template
// moves the derived slots with it instead of silently feeding a shader the wrong
// uniforms.
template <size_t N>
constexpr uint32_t dynamic_offset_count(const DescriptorBindingTemplate (&bindings)[N])
{
uint32_t count = 0;
for (size_t i = 0; i < N; ++i) {
if (bindings[i].type == vk::DescriptorType::eUniformBufferDynamic) {
count += bindings[i].count;
}
}
return count;
}
// Returned by dynamic_offset_slot() when the binding is not a dynamic descriptor of the set.
constexpr uint32_t DYNAMIC_SLOT_INVALID = ~0u;
/**
* @brief Position of a dynamic binding within its set's dynamic-offset array
*
* If @c binding is not a dynamic binding of the given set, @c DYNAMIC_SLOT_INVALID is returned.
* Every use below pairs its constant with a static_assert against that sentinel, so a mistyped or
* non-dynamic binding constant is a compile error rather than a silent 0 — these constants index
* fixed-size dynamic-offset arrays at the bind call sites, so a leaked sentinel would be an
* out-of-bounds write.
*
* Do not "clean this up" back into a `throw` on the not-found path: MSVC rejects a constexpr
* function whose flow analysis can reach a throw (C3615), even when no evaluation takes that path.
*/
template <size_t N>
constexpr uint32_t dynamic_offset_slot(const DescriptorBindingTemplate (&bindings)[N], uint32_t binding)
{
uint32_t slot = 0;
for (size_t i = 0; i < N; ++i) {
if (bindings[i].type != vk::DescriptorType::eUniformBufferDynamic) {
continue;
}
if (bindings[i].binding == binding) {
return slot;
}
slot += bindings[i].count;
}
return DYNAMIC_SLOT_INVALID;
}
// The Global set declares no dynamic bindings; it contributes nothing to a bind call.
constexpr uint32_t GLOBAL_DYNAMIC_OFFSET_COUNT = 0;
constexpr uint32_t MATERIAL_DYNAMIC_OFFSET_COUNT = dynamic_offset_count(MaterialSetBindings);
constexpr uint32_t PERDRAW_DYNAMIC_OFFSET_COUNT = dynamic_offset_count(PerDrawSetBindings);
namespace MaterialDynamicSlot {
constexpr uint32_t ModelData = dynamic_offset_slot(MaterialSetBindings, MaterialBinding::ModelData);
constexpr uint32_t ShadowMapData = dynamic_offset_slot(MaterialSetBindings, MaterialBinding::ShadowMapData);
static_assert(ModelData != DYNAMIC_SLOT_INVALID,
"MaterialBinding::ModelData is not a dynamic descriptor of the Material set.");
static_assert(ShadowMapData != DYNAMIC_SLOT_INVALID,
"MaterialBinding::ShadowMapData is not a dynamic descriptor of the Material set.");
}
namespace PerDrawDynamicSlot {
constexpr uint32_t GenericData = dynamic_offset_slot(PerDrawSetBindings, PerDrawBinding::GenericData);
constexpr uint32_t Matrices = dynamic_offset_slot(PerDrawSetBindings, PerDrawBinding::Matrices);
static_assert(GenericData != DYNAMIC_SLOT_INVALID,
"PerDrawBinding::GenericData is not a dynamic descriptor of the PerDraw set.");
static_assert(Matrices != DYNAMIC_SLOT_INVALID,
"PerDrawBinding::Matrices is not a dynamic descriptor of the PerDraw set.");
}
/**
* @brief Stack-allocated batch writer for descriptor set updates.
*
* Usage: reset() + writeSet() (pre-fills all bindings with fallbacks)
* + setBuffer/setImage overrides for real data + flush().
*
* Bindings declared as eUniformBufferDynamic get their offset split out of the
* descriptor and into the dynamic-offset array (see dynamicOffsets()): setBuffer
* writes {buffer, 0, range} into the descriptor and stashes the caller's offset.
* That is what lets a set whose only per-draw change is a UBO offset be written
* once and rebound many times. Callers must hand dynamicOffsets()/
* VulkanDescriptorManager::getDynamicOffsetCount() for a set to
* vkCmdBindDescriptorSets when binding it.
*/
class DescriptorWriter {
public:
// Fixed capacities for the stack-allocated staging arrays below — NOT Vulkan
// hardware limits. They bound a single vkUpdateDescriptorSets batch (one
// writeSet), sized to comfortably cover the largest descriptor set layout the
// engine builds. Because the backing arrays live on the stack (see m_writes
// etc.), the numbers carry no runtime cost beyond that fixed footprint; the
// only failure mode is under-sizing, which is Assert-guarded at fill time in
// DescriptorWriter::writeSet (e.g. `Assert(m_writeCount < MAX_WRITES)`) so an
// oversized layout trips loudly in debug rather than silently overflowing.
// Raise these if a future set layout legitimately needs more bindings/infos.
static constexpr uint32_t MAX_WRITES = 32; // distinct bindings written per batch
static constexpr uint32_t MAX_BUFFER_INFOS = 20; // buffer descriptors staged per batch
static constexpr uint32_t MAX_IMAGE_INFOS = 24; // image/sampler descriptors staged per batch
static constexpr uint32_t MAX_ACCEL_STRUCT_INFOS = 2; // TLAS descriptors staged per batch (RT)
static constexpr uint32_t MAX_BINDINGS_PER_SET = 16; // highest binding number addressable in a set
// Most dynamic (eUniformBufferDynamic) bindings any one set layout declares. Derived from
// the templates rather than counted by hand, so adding a dynamic binding resizes the
// storage that holds its offset automatically.
static constexpr uint32_t MAX_DYNAMIC_OFFSETS_PER_SET =
MATERIAL_DYNAMIC_OFFSET_COUNT > PERDRAW_DYNAMIC_OFFSET_COUNT ? MATERIAL_DYNAMIC_OFFSET_COUNT
: PERDRAW_DYNAMIC_OFFSET_COUNT;
void reset(vk::Device device, const DescriptorFallbacks& fallbacks) {
m_device = device;
m_fallbacks = &fallbacks;
m_writeCount = 0;
m_bufferInfoCount = 0;
m_imageInfoCount = 0;
m_accelStructInfoCount = 0;
m_dynOffsets = {};
}
void writeSet(DescriptorSetIndex setIndex, vk::DescriptorSet set);
void setBuffer(uint32_t binding, const vk::DescriptorBufferInfo& info);
void setImage(uint32_t binding, const vk::DescriptorImageInfo& info);
void setImageArray(uint32_t binding, ArrayView<vk::DescriptorImageInfo> infos);
/**
* @brief Dynamic offsets accumulated for a single set, ordered by binding number.
*
* Only meaningful for a set this writer actually wrote; a set reused from a
* memoization cache was not written here, so its caller owns the offsets.
* Always sized to the set layout's dynamic descriptor count (offsets for
* bindings left at their fallback stay 0). Points at stable per-set storage —
* valid until the next writeSet() of that set.
*/
const uint32_t* dynamicOffsets(DescriptorSetIndex setIndex) const
{
return m_dynOffsets[static_cast<size_t>(setIndex)].data();
}
/**
* @brief Binds a contiguous run of sets in one vkCmdBindDescriptorSets call
*
* Concatenates the run's dynamic offsets in set order, which is the layout
* vkCmdBindDescriptorSets expects: each set contributes exactly its layout's
* dynamic descriptor count (a set declaring none contributes nothing, rather
* than padding). @c sets must list the sets for @c firstSet onwards, in order.
*
* Binding here rather than handing the offsets back to the caller keeps the
* concatenation buffer's lifetime contained: it is writer-owned scratch, reused
* by the next call, and never outlives the command it was built for.
*/
void bindSets(vk::CommandBuffer cmd,
vk::PipelineLayout layout,
DescriptorSetIndex firstSet,
ArrayView<vk::DescriptorSet> sets);
void flush(); // defined in VulkanDescriptorManager.cpp (reports write stats to the manager)
private:
// Per-binding lookup for the current writeSet, indexed by binding number.
// Populated by writeSet, used by setBuffer/setImage/setImageArray for O(1) access.
struct BindingSlot {
vk::DescriptorBufferInfo* bufferInfo = nullptr; // non-null for buffer bindings
vk::DescriptorImageInfo* imageInfo = nullptr; // non-null for image bindings
uint32_t count = 0; // descriptor count (1 or 16 for arrays)
vk::ImageViewType viewType = vk::ImageViewType::e2D; // for fallback lookup
int dynIndex = -1; // slot in m_dynOffsets, or -1 if not dynamic
};
vk::Device m_device;
const DescriptorFallbacks* m_fallbacks = nullptr;
std::array<vk::WriteDescriptorSet, MAX_WRITES> m_writes;
std::array<vk::DescriptorBufferInfo, MAX_BUFFER_INFOS> m_bufferInfos;
std::array<vk::DescriptorImageInfo, MAX_IMAGE_INFOS> m_imageInfos;
std::array<vk::AccelerationStructureKHR, MAX_ACCEL_STRUCT_INFOS> m_accelStructInfos;
std::array<vk::WriteDescriptorSetAccelerationStructureKHR, MAX_ACCEL_STRUCT_INFOS> m_asWriteInfos;
std::array<BindingSlot, MAX_BINDINGS_PER_SET> m_bindingSlots;
std::array<std::array<uint32_t, MAX_DYNAMIC_OFFSETS_PER_SET>, static_cast<size_t>(DescriptorSetIndex::Count)>
m_dynOffsets{};
// Scratch used by bindSets() to concatenate a run of sets' offsets.
std::array<uint32_t, static_cast<size_t>(DescriptorSetIndex::Count) * MAX_DYNAMIC_OFFSETS_PER_SET>
m_dynOffsetScratch{};
DescriptorSetIndex m_currentSet = DescriptorSetIndex::Global;
uint32_t m_writeCount = 0;
uint32_t m_bufferInfoCount = 0;
uint32_t m_imageInfoCount = 0;
uint32_t m_accelStructInfoCount = 0;
};
/**
* @brief Manages Vulkan descriptor sets, pools, and layouts
@@ -375,19 +195,8 @@ public:
// larger chunks waste memory, smaller chunks allocate pools more often.
// MAX_SETS_PER_POOL supports ~330 draw calls (3 sets each) per chunk; the per-
// type multipliers cover the worst-case bindings a single set can request.
// One chunk now covers a dense scene outright: with the dynamic-UBO descriptor
// memoization in applyMaterial/renderShadowDraw, a big asteroid field at ~1350
// model draws settles at ~250 sets/frame, so growth is back to being the rare
// case it was meant to be. (The same scene needed ~2500 sets/frame before that.)
// Deliberately left at the original size rather than raised: the per-frame
// growth this used to log was a symptom of the churn, not of undersizing, and
// FSO targets low-end GPUs where a larger pool is real wasted memory.
static constexpr uint32_t MAX_SETS_PER_POOL = 1024;
// Worst case per set is 3 static UBOs (Global, PerDraw) and 2 dynamic (Material:
// ModelData + ShadowMapData; PerDraw: GenericData + Matrices). Total UBO
// capacity per chunk is slightly below what this was before the dynamic split.
static constexpr uint32_t MAX_UNIFORM_BUFFERS_PER_POOL = MAX_SETS_PER_POOL * 5; // static UBOs
static constexpr uint32_t MAX_DYNAMIC_UNIFORM_BUFFERS_PER_POOL = MAX_SETS_PER_POOL * 2; // dynamic UBOs
static constexpr uint32_t MAX_UNIFORM_BUFFERS_PER_POOL = MAX_SETS_PER_POOL * 9; // up to 9 UBOs per draw
static constexpr uint32_t MAX_SAMPLERS_PER_POOL = MAX_SETS_PER_POOL * 16; // up to 16 samplers per material set
VulkanDescriptorManager() = default;
@@ -445,15 +254,6 @@ public:
*/
static const DescriptorSetTemplate& getSetTemplate(DescriptorSetIndex setIndex);
/**
* @brief Number of dynamic (eUniformBufferDynamic) descriptors in a set layout
*
* vkCmdBindDescriptorSets requires exactly this many entries in pDynamicOffsets,
* so every bind site must agree with the layout. VulkanStateTracker asserts on
* it; raw cmd.bindDescriptorSets() callers should pass the matching count.
*/
static uint32_t getDynamicOffsetCount(DescriptorSetIndex setIndex);
/**
* @brief Get descriptor set layout for a given set index
*/
+58 -139
View File
@@ -686,12 +686,8 @@ void VulkanDrawManager::renderModel(model_material* material_info, indexed_verte
stateTracker->getCommandBuffer().drawIndexed(dp.indexCount, 1, dp.firstIndex, dp.baseVertex, 0);
}
void VulkanDrawManager::renderShadowDraw(gr_buffer_handle ubo_handle,
size_t ubo_offset,
size_t ubo_size,
vertex_buffer* buffer,
indexed_vertex_source* vert_src,
size_t texi)
void VulkanDrawManager::renderShadowDraw(gr_buffer_handle ubo_handle, size_t ubo_offset, size_t ubo_size,
vertex_buffer* buffer, indexed_vertex_source* vert_src, size_t texi) const
{
if (!buffer || !vert_src) {
return;
@@ -752,96 +748,57 @@ void VulkanDrawManager::renderShadowDraw(gr_buffer_handle ubo_handle,
}
stateTracker->bindPipeline(pipeline, pipelineManager->getPipelineLayout());
stateTracker->setDepthBias(1024.0f, 1.0f); // GL shadow pass: gr_zbias(-1024) = polygon offset factor 1, units 1024
stateTracker->setDepthBias(-1024.0f, 0.0f);
// Bind descriptor sets: shadowMapData (per-draw) and shadowCascadeParams
// (bound separately, per-frame, via shadow_cascade_params_bind) both live in
// the fixed 3-tier layout alongside the batched-submodel transform buffer;
// no material textures are needed for depth-only rendering.
//
// All three sets are memoized across the shadow pass (see m_cachedShadowSets).
// ShadowMapData is a dynamic binding, so the only thing that changes from one
// shadow draw to the next -- its UBO offset -- rides in the dynamic-offset array
// and leaves the set contents untouched. Without that this path allocated and
// fully rewrote three descriptor sets per shadow draw, times the cascade count.
{
ShadowSetInputs shadowInputs;
DescriptorWriter writer;
writer.reset(descManager->getDevice(), descManager->getFallbacks());
vk::DescriptorSet globalSet = descManager->allocateFrameSet(DescriptorSetIndex::Global);
Assert(globalSet);
writer.writeSet(globalSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::Global));
{
const auto& cascade =
getPendingUniformBinding(static_cast<size_t>(uniform_block_type::ShadowCascadeParams));
shadowInputs.cascadeHandle = cascade.bufferHandle.value();
shadowInputs.cascadeOffset = cascade.offset;
shadowInputs.cascadeSize = cascade.size;
shadowInputs.cascadeValid = cascade.valid;
shadowInputs.shadowDataHandle = ubo_handle.value();
shadowInputs.shadowDataSize = ubo_size; // offset is dynamic, so not part of the key
auto& tf = g_transformBuffers[descManager->getCurrentFrame()];
const auto& pending = getPendingUniformBinding(static_cast<size_t>(uniform_block_type::ShadowCascadeParams));
if (pending.valid) {
vk::Buffer buf = bufferManager->getVkBuffer(pending.bufferHandle);
if (buf) {
writer.setBuffer(GlobalBinding::ShadowCascadeParams, {buf, pending.offset, pending.size});
}
}
}
vk::DescriptorSet materialSet = descManager->allocateFrameSet(DescriptorSetIndex::Material);
Assert(materialSet);
writer.writeSet(materialSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::Material));
{
vk::Buffer buf = bufferManager->getVkBuffer(ubo_handle);
if (buf) {
writer.setBuffer(MaterialBinding::ShadowMapData,
{buf, static_cast<vk::DeviceSize>(ubo_offset), static_cast<vk::DeviceSize>(ubo_size)});
}
}
{
uint32_t tfIdx = descManager->getCurrentFrame();
auto& tf = g_transformBuffers[tfIdx];
if (tf.buffer && tf.lastUploadSize > 0) {
shadowInputs.transformBuffer = tf.buffer;
shadowInputs.transformOffset = tf.lastUploadOffset;
shadowInputs.transformSize = tf.lastUploadSize;
writer.setBuffer(MaterialBinding::TransformSSBO, {tf.buffer,
static_cast<vk::DeviceSize>(tf.lastUploadOffset),
static_cast<vk::DeviceSize>(tf.lastUploadSize)});
}
}
if (!m_cachedShadowValid || shadowInputs != m_cachedShadowInputs) {
DescriptorWriter writer;
writer.reset(descManager->getDevice(), descManager->getFallbacks());
vk::DescriptorSet perDrawSet = descManager->allocateFrameSet(DescriptorSetIndex::PerDraw);
Assert(perDrawSet);
writer.writeSet(perDrawSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::PerDraw));
writer.flush();
m_cachedShadowGlobalSet = descManager->allocateFrameSet(DescriptorSetIndex::Global);
Assert(m_cachedShadowGlobalSet);
writer.writeSet(DescriptorSetIndex::Global, m_cachedShadowGlobalSet);
if (shadowInputs.cascadeValid) {
vk::Buffer buf = bufferManager->getVkBuffer(gr_buffer_handle(shadowInputs.cascadeHandle));
if (buf) {
writer.setBuffer(GlobalBinding::ShadowCascadeParams,
{buf, shadowInputs.cascadeOffset, shadowInputs.cascadeSize});
}
}
m_cachedShadowMaterialSet = descManager->allocateFrameSet(DescriptorSetIndex::Material);
Assert(m_cachedShadowMaterialSet);
writer.writeSet(DescriptorSetIndex::Material, m_cachedShadowMaterialSet);
{
vk::Buffer buf = bufferManager->getVkBuffer(ubo_handle);
if (buf) {
// Offset 0: the real offset is supplied per draw as a dynamic offset.
writer.setBuffer(MaterialBinding::ShadowMapData, {buf, 0, static_cast<vk::DeviceSize>(ubo_size)});
}
}
if (shadowInputs.transformBuffer) {
writer.setBuffer(MaterialBinding::TransformSSBO,
{shadowInputs.transformBuffer,
static_cast<vk::DeviceSize>(shadowInputs.transformOffset),
static_cast<vk::DeviceSize>(shadowInputs.transformSize)});
}
m_cachedShadowPerDrawSet = descManager->allocateFrameSet(DescriptorSetIndex::PerDraw);
Assert(m_cachedShadowPerDrawSet);
writer.writeSet(DescriptorSetIndex::PerDraw, m_cachedShadowPerDrawSet);
writer.flush();
m_cachedShadowInputs = shadowInputs;
m_cachedShadowValid = true;
}
// applyMaterial's caches deliberately are NOT invalidated here. They record
// what a descriptor set *contains*, and the shadow pass allocates its own
// sets rather than overwriting theirs. Which set is currently bound is
// tracked separately by VulkanStateTracker, and this path binds through it,
// so the next applyMaterial cache hit still rebinds its set correctly. (That
// is what separates this from the ImGui case invalidateDrawStateCaches()
// exists for: ImGui binds on the command buffer behind the tracker's back.)
// ShadowMapData is the one thing that moves per shadow draw; every other dynamic binding
// on this path holds the fallback buffer and so must stay at offset 0.
uint32_t materialDynOffsets[MATERIAL_DYNAMIC_OFFSET_COUNT] = {};
materialDynOffsets[MaterialDynamicSlot::ShadowMapData] = static_cast<uint32_t>(ubo_offset);
static constexpr uint32_t perDrawDynOffsets[PERDRAW_DYNAMIC_OFFSET_COUNT] = {};
stateTracker->bindDescriptorSet(DescriptorSetIndex::Global, m_cachedShadowGlobalSet, nullptr);
stateTracker->bindDescriptorSet(DescriptorSetIndex::Material, m_cachedShadowMaterialSet, materialDynOffsets);
stateTracker->bindDescriptorSet(DescriptorSetIndex::PerDraw, m_cachedShadowPerDrawSet, perDrawDynOffsets);
stateTracker->bindDescriptorSet(DescriptorSetIndex::Global, globalSet);
stateTracker->bindDescriptorSet(DescriptorSetIndex::Material, materialSet);
stateTracker->bindDescriptorSet(DescriptorSetIndex::PerDraw, perDrawSet);
}
vk::Buffer vbuffer = bufferManager->getVkBuffer(vert_src->Vbuffer_handle);
@@ -1003,10 +960,6 @@ void VulkanDrawManager::resetFrameStats()
m_cachedMaterialValid = false;
m_cachedPerDrawSet = nullptr;
m_cachedPerDrawValid = false;
m_cachedShadowGlobalSet = nullptr;
m_cachedShadowMaterialSet = nullptr;
m_cachedShadowPerDrawSet = nullptr;
m_cachedShadowValid = false;
}
void VulkanDrawManager::printFrameStats()
@@ -1330,7 +1283,6 @@ bool VulkanDrawManager::applyMaterial(material* mat, primitive_type prim_type, v
// Like opengl_tnl_set_material(), every material draw sets the texture addressing (e.g. MR_FORCE_CLAMP skyboxes)
setTextureAddressing(mat->get_texture_addressing());
vulkan_zbias(mat->get_depth_bias());
// Build pipeline configuration from material
PipelineConfig config = buildPipelineConfig(mat, prim_type);
@@ -1396,28 +1348,6 @@ bool VulkanDrawManager::applyMaterial(material* mat, primitive_type prim_type, v
}
};
// Dynamic offsets for the Material/PerDraw sets, ordered by binding number.
// Computed here rather than read back from the writer because the whole point
// of the dynamic bindings is that these change on draws where the set itself
// is reused from cache and never written.
const auto dynOffsetOf = [&](uniform_block_type blockType) -> uint32_t {
const auto& pending = m_pendingUniformBindings[static_cast<size_t>(blockType)];
if (!pending.valid || !bufferManager->getVkBuffer(pending.bufferHandle)) {
return 0; // falls back to the dummy buffer, which is bound at offset 0
}
return static_cast<uint32_t>(pending.offset);
};
// Zero-initialized, then filled by name: MaterialDynamicSlot::ShadowMapData is left at 0
// because ShadowMapData is never bound on this path, so its descriptor still holds the
// fallback buffer -- bound as {buffer, 0, FALLBACK_UNIFORM_BUFFER_SIZE}, i.e. spanning the
// whole buffer, so any non-zero dynamic offset would push offset+range past the end of it.
uint32_t materialDynOffsets[MATERIAL_DYNAMIC_OFFSET_COUNT] = {};
materialDynOffsets[MaterialDynamicSlot::ModelData] = dynOffsetOf(uniform_block_type::ModelData);
uint32_t perDrawDynOffsets[PERDRAW_DYNAMIC_OFFSET_COUNT] = {};
perDrawDynOffsets[PerDrawDynamicSlot::GenericData] = dynOffsetOf(uniform_block_type::GenericData);
perDrawDynOffsets[PerDrawDynamicSlot::Matrices] = dynOffsetOf(uniform_block_type::Matrices);
// Set 0: Global (memoized per frame — see m_cachedGlobalSet). Rebuilt only
// when a Global input changed: a pending Global UBO (m_globalSetDirty set in
// setPendingUniformBinding), the shadow TLAS (invalidateGlobalSet from
@@ -1429,7 +1359,7 @@ bool VulkanDrawManager::applyMaterial(material* mat, primitive_type prim_type, v
if (m_globalSetDirty || !m_cachedGlobalSet || shadowReady != m_cachedGlobalHadShadow) {
m_cachedGlobalSet = descManager->allocateFrameSet(DescriptorSetIndex::Global);
Assert(m_cachedGlobalSet);
writer.writeSet(DescriptorSetIndex::Global, m_cachedGlobalSet);
writer.writeSet(m_cachedGlobalSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::Global));
bindPendingUBOs(DescriptorSetIndex::Global);
if (shadowReady) {
writer.setImage(GlobalBinding::ShadowMap, pp->getShadowTextureInfo());
@@ -1466,14 +1396,9 @@ bool VulkanDrawManager::applyMaterial(material* mat, primitive_type prim_type, v
matInputs.sceneColorInfo = m_sceneColorInfo;
matInputs.distMapInfo = m_distMapInfo;
{
// ModelData's offset is deliberately NOT part of the key: it is a dynamic
// binding, so an offset-only change is carried by materialDynOffsets and
// leaves the set's contents identical. Buffer and size still matter --
// both live in the descriptor.
const auto& md = m_pendingUniformBindings[static_cast<size_t>(uniform_block_type::ModelData)];
const auto& dg = m_pendingUniformBindings[static_cast<size_t>(uniform_block_type::DecalGlobals)];
matInputs.uboHandle[0] = md.bufferHandle.value();
matInputs.uboOffset[0] = 0;
matInputs.uboHandle[0] = md.bufferHandle.value(); matInputs.uboOffset[0] = md.offset;
matInputs.uboSize[0] = md.size; matInputs.uboValid[0] = md.valid;
matInputs.uboHandle[1] = dg.bufferHandle.value(); matInputs.uboOffset[1] = dg.offset;
matInputs.uboSize[1] = dg.size; matInputs.uboValid[1] = dg.valid;
@@ -1487,7 +1412,7 @@ bool VulkanDrawManager::applyMaterial(material* mat, primitive_type prim_type, v
} else {
materialSet = descManager->allocateFrameSet(DescriptorSetIndex::Material);
Assert(materialSet);
writer.writeSet(DescriptorSetIndex::Material, materialSet);
writer.writeSet(materialSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::Material));
bindPendingUBOs(DescriptorSetIndex::Material);
if (matInputs.transformBuffer) {
writer.setBuffer(MaterialBinding::TransformSSBO,
@@ -1507,23 +1432,14 @@ bool VulkanDrawManager::applyMaterial(material* mat, primitive_type prim_type, v
// Set 2: PerDraw (previous-set memoized — see m_cachedPerDrawSet)
PerDrawSetInputs pdInputs;
{
struct PerDrawUbo {
uniform_block_type blockType;
uint32_t binding;
};
static constexpr PerDrawUbo pdUbos[NUM_PERDRAW_UBOS] = {
{uniform_block_type::GenericData, PerDrawBinding::GenericData},
{uniform_block_type::Matrices, PerDrawBinding::Matrices},
{uniform_block_type::NanoVGData, PerDrawBinding::NanoVGData},
{uniform_block_type::DecalInfo, PerDrawBinding::DecalInfo},
{uniform_block_type::MovieData, PerDrawBinding::MovieData}};
static constexpr uniform_block_type pdTypes[NUM_PERDRAW_UBOS] = {
uniform_block_type::GenericData, uniform_block_type::Matrices,
uniform_block_type::NanoVGData, uniform_block_type::DecalInfo,
uniform_block_type::MovieData};
for (int i = 0; i < NUM_PERDRAW_UBOS; ++i) {
const auto& p = m_pendingUniformBindings[static_cast<size_t>(pdUbos[i].blockType)];
const auto& p = m_pendingUniformBindings[static_cast<size_t>(pdTypes[i])];
pdInputs.uboHandle[i] = p.bufferHandle.value();
// The offset of a dynamic binding rides in perDrawDynOffsets and is not part
// of the set's contents.
const bool dynamic = dynamic_offset_slot(PerDrawSetBindings, pdUbos[i].binding) != DYNAMIC_SLOT_INVALID;
pdInputs.uboOffset[i] = dynamic ? 0 : p.offset;
pdInputs.uboOffset[i] = p.offset;
pdInputs.uboSize[i] = p.size;
pdInputs.uboValid[i] = p.valid;
}
@@ -1535,16 +1451,16 @@ bool VulkanDrawManager::applyMaterial(material* mat, primitive_type prim_type, v
} else {
perDrawSet = descManager->allocateFrameSet(DescriptorSetIndex::PerDraw);
Assert(perDrawSet);
writer.writeSet(DescriptorSetIndex::PerDraw, perDrawSet);
writer.writeSet(perDrawSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::PerDraw));
bindPendingUBOs(DescriptorSetIndex::PerDraw);
m_cachedPerDrawSet = perDrawSet;
m_cachedPerDrawInputs = pdInputs;
m_cachedPerDrawValid = true;
}
writer.flush();
stateTracker->bindDescriptorSet(DescriptorSetIndex::Global, globalSet, nullptr);
stateTracker->bindDescriptorSet(DescriptorSetIndex::Material, materialSet, materialDynOffsets);
stateTracker->bindDescriptorSet(DescriptorSetIndex::PerDraw, perDrawSet, perDrawDynOffsets);
stateTracker->bindDescriptorSet(DescriptorSetIndex::Global, globalSet);
stateTracker->bindDescriptorSet(DescriptorSetIndex::Material, materialSet);
stateTracker->bindDescriptorSet(DescriptorSetIndex::PerDraw, perDrawSet);
}
// Update tracked state for FSO compatibility
@@ -1567,6 +1483,9 @@ bool VulkanDrawManager::applyMaterial(material* mat, primitive_type prim_type, v
stateTracker->setStencilMode(GR_STENCIL_NONE);
}
// Set depth bias if needed
stateTracker->setDepthBias(static_cast<float>(mat->get_depth_bias()), 0.0f);
return true;
}
@@ -1624,7 +1543,7 @@ void VulkanDrawManager::draw(primitive_type prim_type, int first_vertex, int ver
m_frameStats.totalVertices += vertex_count;
// Flush any dirty dynamic state (viewport, scissor, depth bias, stencil ref)
// before issuing the draw command. applyMaterial sets some of these AFTER bindPipeline,
// before issuing the draw command. applyMaterial sets these AFTER bindPipeline,
// so they may be dirty even when the pipeline didn't change.
stateTracker->applyDynamicState();
+1 -64
View File
@@ -212,7 +212,7 @@ class VulkanDrawManager {
size_t ubo_size,
vertex_buffer* buffer,
indexed_vertex_source* vert_src,
size_t texi);
size_t texi) const;
/**
* @brief Draw a unit sphere with the given material
@@ -286,26 +286,6 @@ class VulkanDrawManager {
*/
void invalidateGlobalSet() { m_globalSetDirty = true; }
/**
* @brief Invalidate every memoized descriptor-set cache (Global + Material + PerDraw)
*
* These previous-set caches (see the class comments above m_cachedGlobalSet /
* m_cachedMaterialSet / m_cachedPerDrawSet) assume nothing rebinds a *different*
* descriptor set on the command buffer between applyMaterial() calls behind their
* back. Code that renders directly on the command buffer without going through
* applyMaterial (currently: ImGui's Vulkan backend, drawing into the same active
* composition pass) breaks that assumption -- call this right afterward, alongside
* VulkanStateTracker::invalidateExternalBindings(), so the next applyMaterial() rebuilds
* and rebinds every set instead of trusting stale cached handles.
*/
void invalidateDrawStateCaches()
{
m_globalSetDirty = true;
m_cachedMaterialValid = false;
m_cachedPerDrawValid = false;
m_cachedShadowValid = false;
}
/**
* @brief Get current texture addressing mode
*/
@@ -469,14 +449,6 @@ class VulkanDrawManager {
mutable FrameStats m_frameStats;
int m_frameStatsFrameNum = 0;
public:
/**
* @brief Read-only access to this frame's diagnostic counters, e.g. for the ImGui profiler
* overlay's -gr_debug section (see printFrameStats() for the nprintf equivalent)
*/
const FrameStats& getFrameStats() const { return m_frameStats; }
private:
// First-N debug-log counter for on-demand texture binds; a member rather
// than a function-local static so it resets on renderer restart. mutable because
// bindMaterialTextures is const. Gates nprintf spam only.
@@ -568,41 +540,6 @@ class VulkanDrawManager {
PerDrawSetInputs m_cachedPerDrawInputs;
bool m_cachedPerDrawValid = false;
// ---- Shadow-pass descriptor memoization (renderShadowDraw) ----
// The shadow pass doesn't go through applyMaterial, so it gets its own cache.
// All three of its sets are constant across the whole pass: the only per-draw
// input, the ShadowMapData UBO offset, is a dynamic binding and therefore not
// part of the set contents. Reset per frame with the rest (resetFrameStats).
struct ShadowSetInputs {
int cascadeHandle = -1;
vk::DeviceSize cascadeOffset = 0;
vk::DeviceSize cascadeSize = 0;
bool cascadeValid = false;
int shadowDataHandle = -1;
size_t shadowDataSize = 0;
vk::Buffer transformBuffer = nullptr;
size_t transformOffset = 0;
size_t transformSize = 0;
bool operator==(const ShadowSetInputs& o) const
{
return cascadeHandle == o.cascadeHandle && cascadeOffset == o.cascadeOffset &&
cascadeSize == o.cascadeSize && cascadeValid == o.cascadeValid &&
shadowDataHandle == o.shadowDataHandle && shadowDataSize == o.shadowDataSize &&
transformBuffer == o.transformBuffer && transformOffset == o.transformOffset &&
transformSize == o.transformSize;
}
bool operator!=(const ShadowSetInputs& o) const
{
return !(*this == o);
}
};
vk::DescriptorSet m_cachedShadowGlobalSet = nullptr;
vk::DescriptorSet m_cachedShadowMaterialSet = nullptr;
vk::DescriptorSet m_cachedShadowPerDrawSet = nullptr;
ShadowSetInputs m_cachedShadowInputs;
bool m_cachedShadowValid = false;
// Texture overrides for material bindings 4-6.
vk::DescriptorImageInfo m_depthTextureInfo; // binding 4: depth/position for soft particles
vk::DescriptorImageInfo m_sceneColorInfo; // binding 5: scene color for distortion
+11 -14
View File
@@ -98,7 +98,8 @@ void vulkan_set_default_material_uniforms(material* material_info)
(clip_plane.normal.xyz.y * clip_plane.position.xyz.y) +
(clip_plane.normal.xyz.z * clip_plane.position.xyz.z));
data->modelMatrix = gr_model_matrix_stack.get_transform();
// Model matrix (identity for now, material doesn't provide one)
vm_matrix4_set_identity(&data->modelMatrix);
} else {
data->clipEnabled = 0;
vm_matrix4_set_identity(&data->modelMatrix);
@@ -612,9 +613,6 @@ void vulkan_calculate_irrmap()
}
// Get envmap cubemap view
if (!texManager->ensureCubemapUploaded(ENVMAP)) {
return;
}
auto* envSlot = bm_get_slot(ENVMAP, true);
if (!envSlot || !envSlot->gr_info) {
return;
@@ -640,6 +638,9 @@ void vulkan_calculate_irrmap()
vk::CommandBuffer cmd = stateTracker->getCommandBuffer();
// End the current swap chain render pass
cmd.endRenderPass();
// Create pipeline for irradiance map generation
PipelineConfig config;
config.shaderType = SDR_TYPE_IRRADIANCE_MAP_GEN;
@@ -710,9 +711,6 @@ void vulkan_calculate_irrmap()
vk::Extent2D irrExtent(irrTs->width, irrTs->height);
// End the swap chain pass only after the early returns above, which never resume it
cmd.endRenderPass();
for (size_t face = 0; face < irrTs->cubeFaceFramebuffers.size(); face++) {
vk::Framebuffer fb = irrTs->cubeFaceFramebuffers[face];
if (!fb) {
@@ -755,16 +753,15 @@ void vulkan_calculate_irrmap()
// Set 0: Global (all fallback)
vk::DescriptorSet globalSet = descManager->allocateFrameSet(DescriptorSetIndex::Global);
Assert(globalSet);
writer.writeSet(DescriptorSetIndex::Global, globalSet);
writer.writeSet(globalSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::Global));
// Set 1: Material (envmap cubemap at element 0 of texture array)
vk::DescriptorSet materialSet = descManager->allocateFrameSet(DescriptorSetIndex::Material);
Assert(materialSet);
writer.writeSet(DescriptorSetIndex::Material, materialSet);
writer.writeSet(materialSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::Material));
{
std::array<vk::DescriptorImageInfo, VulkanDescriptorManager::MAX_TEXTURE_BINDINGS> texImages;
// irrmap-f.sdr declares this binding as samplerCube, so every element needs a cube view
texImages.fill(descManager->getFallbacks().textureCube);
texImages.fill(descManager->getFallbacks().texture2D);
texImages[0].imageView = envmapView;
writer.setImageArray(MaterialBinding::TextureArray, texImages);
}
@@ -772,14 +769,14 @@ void vulkan_calculate_irrmap()
// Set 2: PerDraw (face UBO at binding 0)
vk::DescriptorSet perDrawSet = descManager->allocateFrameSet(DescriptorSetIndex::PerDraw);
Assert(perDrawSet);
writer.writeSet(DescriptorSetIndex::PerDraw, perDrawSet);
writer.writeSet(perDrawSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::PerDraw));
writer.setBuffer(PerDrawBinding::GenericData, {faceUBO,
static_cast<vk::DeviceSize>(face) * UBO_SLOT_SIZE, UBO_SLOT_SIZE});
writer.flush();
// Bind all descriptor sets
const vk::DescriptorSet sets[] = {globalSet, materialSet, perDrawSet};
writer.bindSets(cmd, pipelineLayout, DescriptorSetIndex::Global, sets);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout,
0, {globalSet, materialSet, perDrawSet}, {});
// Draw fullscreen triangle
cmd.draw(3, 1, 0, 0);
+2 -10
View File
@@ -140,8 +140,7 @@ VmaMemoryUsage VulkanMemoryManager::toVmaUsage(MemoryUsage usage)
}
}
bool VulkanMemoryManager::allocateBufferMemory(vk::Buffer buffer, MemoryUsage usage, VulkanAllocation& allocation,
MemoryPurpose purpose)
bool VulkanMemoryManager::allocateBufferMemory(vk::Buffer buffer, MemoryUsage usage, VulkanAllocation& allocation)
{
if (!m_initialized) {
nprintf(("vulkan", "VulkanMemoryManager::allocateBufferMemory called before initialization!\n"));
@@ -177,17 +176,14 @@ bool VulkanMemoryManager::allocateBufferMemory(vk::Buffer buffer, MemoryUsage us
allocation.size = allocInfo.size;
allocation.mappedPtr = allocInfo.pMappedData;
allocation.purpose = purpose;
++m_allocationCount;
m_totalAllocatedBytes += static_cast<size_t>(allocation.size);
m_bytesByPurpose[static_cast<size_t>(purpose)] += static_cast<size_t>(allocation.size);
return true;
}
bool VulkanMemoryManager::allocateImageMemory(vk::Image image, MemoryUsage usage, VulkanAllocation& allocation,
MemoryPurpose purpose)
bool VulkanMemoryManager::allocateImageMemory(vk::Image image, MemoryUsage usage, VulkanAllocation& allocation)
{
if (!m_initialized) {
nprintf(("vulkan", "VulkanMemoryManager::allocateImageMemory called before initialization!\n"));
@@ -223,11 +219,9 @@ bool VulkanMemoryManager::allocateImageMemory(vk::Image image, MemoryUsage usage
allocation.size = allocInfo.size;
allocation.mappedPtr = allocInfo.pMappedData;
allocation.purpose = purpose;
++m_allocationCount;
m_totalAllocatedBytes += static_cast<size_t>(allocation.size);
m_bytesByPurpose[static_cast<size_t>(purpose)] += static_cast<size_t>(allocation.size);
return true;
}
@@ -248,12 +242,10 @@ void VulkanMemoryManager::freeAllocation(VulkanAllocation& allocation)
--m_allocationCount;
m_totalAllocatedBytes -= static_cast<size_t>(allocation.size);
m_bytesByPurpose[static_cast<size_t>(allocation.purpose)] -= static_cast<size_t>(allocation.size);
allocation.vmaAlloc = VK_NULL_HANDLE;
allocation.size = 0;
allocation.mappedPtr = nullptr;
allocation.purpose = MemoryPurpose::Unknown;
}
void* VulkanMemoryManager::mapMemory(VulkanAllocation& allocation)
+2 -27
View File
@@ -30,19 +30,6 @@ class VulkanRenderer;
// above in lockstep, or VMA's internal assert will fire.
constexpr uint32_t VulkanApiVersion = VK_API_VERSION_1_2;
/**
* @brief What an allocation is used for, for the profiler overlay's per-purpose GPU memory
* breakdown. Unknown covers anything not worth categorizing for that panel (transient staging
* buffers, uniform buffers -- the latter are already tracked separately via
* UniformBufferManager/gr_debug_stats).
*/
enum class MemoryPurpose {
Unknown,
Texture,
Geometry,
RenderTarget
};
/**
* @brief Memory allocation info returned when allocating GPU memory.
*
@@ -53,7 +40,6 @@ struct VulkanAllocation {
VmaAllocation vmaAlloc = VK_NULL_HANDLE;
vk::DeviceSize size = 0;
void* mappedPtr = nullptr; // Non-null if memory is persistently mapped
MemoryPurpose purpose = MemoryPurpose::Unknown;
bool isValid() const { return vmaAlloc != VK_NULL_HANDLE; }
};
@@ -108,24 +94,18 @@ public:
* @param buffer The buffer to allocate memory for
* @param usage The intended memory usage pattern
* @param[out] allocation Output allocation info
* @param purpose What the buffer is used for, for the per-purpose memory breakdown. Defaults
* to Unknown for callers that don't care about that breakdown (staging buffers, etc).
* @return true on success
*/
bool allocateBufferMemory(vk::Buffer buffer, MemoryUsage usage, VulkanAllocation& allocation,
MemoryPurpose purpose = MemoryPurpose::Unknown);
bool allocateBufferMemory(vk::Buffer buffer, MemoryUsage usage, VulkanAllocation& allocation);
/**
* @brief Allocate memory for an image
* @param image The image to allocate memory for
* @param usage The intended memory usage pattern
* @param[out] allocation Output allocation info
* @param purpose What the image is used for, for the per-purpose memory breakdown. Defaults
* to Unknown for callers that don't care about that breakdown.
* @return true on success
*/
bool allocateImageMemory(vk::Image image, MemoryUsage usage, VulkanAllocation& allocation,
MemoryPurpose purpose = MemoryPurpose::Unknown);
bool allocateImageMemory(vk::Image image, MemoryUsage usage, VulkanAllocation& allocation);
/**
* @brief Free a previous allocation
@@ -168,10 +148,6 @@ public:
size_t getAllocationCount() const { return m_allocationCount; }
size_t getTotalAllocatedBytes() const { return m_totalAllocatedBytes; }
size_t getTextureBytes() const { return m_bytesByPurpose[static_cast<size_t>(MemoryPurpose::Texture)]; }
size_t getGeometryBytes() const { return m_bytesByPurpose[static_cast<size_t>(MemoryPurpose::Geometry)]; }
size_t getRenderTargetBytes() const { return m_bytesByPurpose[static_cast<size_t>(MemoryPurpose::RenderTarget)]; }
private:
static VmaMemoryUsage toVmaUsage(MemoryUsage usage);
@@ -179,7 +155,6 @@ private:
size_t m_allocationCount = 0;
size_t m_totalAllocatedBytes = 0;
size_t m_bytesByPurpose[4] = {}; // indexed by MemoryPurpose
bool m_initialized = false;
};
+1 -18
View File
@@ -394,26 +394,9 @@ vk::UniquePipeline VulkanPipelineManager::createPipeline(const PipelineConfig& c
}
// Input assembly
//
// Primitive restart is enabled for every topology that permits it, rather than disabled
// outright. Metal always performs primitive restart on indexed draws and cannot turn it off, so
// MoltenVK rejects a strip/fan pipeline that asks to disable it -- vkCreateGraphicsPipelines
// fails with VK_ERROR_FEATURE_NOT_PRESENT ("Metal does not support disabling primitive
// restart"). getPipeline() then returns a null pipeline and the draw is skipped, which on macOS
// silently dropped the entire 2D/UI/HUD path (gr_bitmap and friends render quads as
// PRIM_TYPE_TRISTRIP; see graphics/render.cpp) while leaving ImGui -- which builds its own
// TRIANGLE_LIST pipeline -- as the only thing still on screen.
//
// Enabling it changes nothing for FSO's geometry: an index buffer is promoted to 32-bit as soon
// as a mesh reaches USHRT_MAX vertices (VB_FLAG_LARGE_INDEX, see modelinterp.cpp), so 0xFFFF is
// never a real 16-bit index, and 0xFFFFFFFF is unreachable for 32-bit ones. Neither restart
// sentinel can collide with live index data.
//
// List topologies must keep it disabled: the spec only allows primitiveRestartEnable on them
// with VK_EXT_primitive_topology_list_restart, which FSO does not request.
vk::PipelineInputAssemblyStateCreateInfo inputAssembly;
inputAssembly.topology = convertPrimitiveType(config.primitiveType);
inputAssembly.primitiveRestartEnable = topologySupportsPrimitiveRestart(inputAssembly.topology);
inputAssembly.primitiveRestartEnable = VK_FALSE;
// Viewport state (dynamic)
vk::PipelineViewportStateCreateInfo viewportState;
+9 -12
View File
@@ -687,7 +687,7 @@ void VulkanPostProcessor::blitToSwapChain(vk::CommandBuffer cmd)
// Set 1: Material — source texture at array slot 0
vk::DescriptorSet materialSet = descriptorMgr->allocateFrameSet(DescriptorSetIndex::Material);
Assert(materialSet);
writer.writeSet(DescriptorSetIndex::Material, materialSet);
writer.writeSet(materialSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::Material));
{
std::array<vk::DescriptorImageInfo, VulkanDescriptorManager::MAX_TEXTURE_BINDINGS> texArrayInfos;
texArrayInfos.fill(descriptorMgr->getFallbacks().texture2D);
@@ -701,7 +701,7 @@ void VulkanPostProcessor::blitToSwapChain(vk::CommandBuffer cmd)
// Set 2: PerDraw — tonemapping UBO (from the per-frame scratch ring)
vk::DescriptorSet perDrawSet = descriptorMgr->allocateFrameSet(DescriptorSetIndex::PerDraw);
Assert(perDrawSet);
writer.writeSet(DescriptorSetIndex::PerDraw, perDrawSet);
writer.writeSet(perDrawSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::PerDraw));
// LDR path: passthrough (tonemapping already ran). Fallback path: live
// parameters from the engine lighting profile.
@@ -732,12 +732,8 @@ void VulkanPostProcessor::blitToSwapChain(vk::CommandBuffer cmd)
{m_ctx.scratchRing.buffer(), slotOffset, m_ctx.scratchRing.slotSize()});
}
writer.flush();
stateTracker->bindDescriptorSet(DescriptorSetIndex::Material,
materialSet,
writer.dynamicOffsets(DescriptorSetIndex::Material));
stateTracker->bindDescriptorSet(DescriptorSetIndex::PerDraw,
perDrawSet,
writer.dynamicOffsets(DescriptorSetIndex::PerDraw));
stateTracker->bindDescriptorSet(DescriptorSetIndex::Material, materialSet);
stateTracker->bindDescriptorSet(DescriptorSetIndex::PerDraw, perDrawSet);
// Draw fullscreen triangle (3 vertices from gl_VertexIndex, no vertex buffer)
cmd.draw(3, 1, 0, 0);
@@ -804,7 +800,7 @@ void VulkanPostProcessor::encodeToSwapChainPass(vk::CommandBuffer cmd, vk::Rende
vk::DescriptorSet materialSet = descriptorMgr->allocateFrameSet(DescriptorSetIndex::Material);
Assert(materialSet);
writer.writeSet(DescriptorSetIndex::Material, materialSet);
writer.writeSet(materialSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::Material));
{
std::array<vk::DescriptorImageInfo, VulkanDescriptorManager::MAX_TEXTURE_BINDINGS> texArrayInfos;
texArrayInfos.fill(descriptorMgr->getFallbacks().texture2D);
@@ -815,7 +811,7 @@ void VulkanPostProcessor::encodeToSwapChainPass(vk::CommandBuffer cmd, vk::Rende
vk::DescriptorSet perDrawSet = descriptorMgr->allocateFrameSet(DescriptorSetIndex::PerDraw);
Assert(perDrawSet);
writer.writeSet(DescriptorSetIndex::PerDraw, perDrawSet);
writer.writeSet(perDrawSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::PerDraw));
{
vk::DeviceSize slotOffset =
m_ctx.scratchRing.alloc(descriptorMgr->getCurrentFrame(), uboData, uboSize);
@@ -824,8 +820,9 @@ void VulkanPostProcessor::encodeToSwapChainPass(vk::CommandBuffer cmd, vk::Rende
}
writer.flush();
const vk::DescriptorSet sets[] = {materialSet, perDrawSet};
writer.bindSets(cmd, pipelineLayout, DescriptorSetIndex::Material, sets);
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout,
static_cast<uint32_t>(DescriptorSetIndex::Material),
{materialSet, perDrawSet}, {});
cmd.draw(3, 1, 0, 0);
cmd.endRenderPass();
@@ -170,8 +170,7 @@ bool VulkanBloom::createTargets()
return false;
}
if (!m_ctx->memoryManager->allocateImageMemory(
m_tex[i].image, MemoryUsage::GpuOnly, m_tex[i].allocation, MemoryPurpose::RenderTarget)) {
if (!m_ctx->memoryManager->allocateImageMemory(m_tex[i].image, MemoryUsage::GpuOnly, m_tex[i].allocation)) {
nprintf(("vulkan", "VulkanBloom: Failed to allocate bloom image %zu memory!\n", i));
return false;
}
Loaded 100 of 228 files, more files were not shown because too many files have changed in this diff. Show more