mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
Compare commits
36
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61ad242997 | ||
|
|
9f2a41032d | ||
|
|
5b6579ac0a | ||
|
|
49464c56f6 | ||
|
|
4dc0ca7878 | ||
|
|
2b1759e98e | ||
|
|
1d0dd687cd | ||
|
|
705d115214 | ||
|
|
a3b4386b7a | ||
|
|
246a6ee8f0 | ||
|
|
e66fe223bf | ||
|
|
e5b591141c | ||
|
|
81b9edc2e3 | ||
|
|
259a58de60 | ||
|
|
b7e7949413 | ||
|
|
32db880e94 | ||
|
|
03589b49bf | ||
|
|
c58a0cd307 | ||
|
|
0b42d0b6e7 | ||
|
|
030bbaa793 | ||
|
|
8ca1ad9bbc | ||
|
|
499b3fb38b | ||
|
|
12bddb8366 | ||
|
|
c47a8bf351 | ||
|
|
3489da442a | ||
|
|
abf796f75f | ||
|
|
89d69189c8 | ||
|
|
b8bd1daa6e | ||
|
|
bb69b1b9db | ||
|
|
9fb692f12f | ||
|
|
43ba8b859e | ||
|
|
49f4b1d9c7 | ||
|
|
9f55626272 | ||
|
|
670df9b97e | ||
|
|
9a70489dbc | ||
|
|
52fb1bdc8d |
No files matched your search
+13
-4
@@ -57,6 +57,15 @@ 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.")
|
||||
@@ -109,8 +118,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)
|
||||
# On windows and mac the default should be to always build the included libraries
|
||||
IF(WIN32 OR APPLE OR ANDROID)
|
||||
# On windows, mac and android 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)
|
||||
@@ -138,13 +147,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 (External shaders only!)" OFF)
|
||||
OPTION(FSO_BUILD_WITH_OPENGL_ES "When building OpenGL, use OpenGL ES compatibility layer" 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 FORCE)
|
||||
set(FSO_BUILD_WITH_OPENGL_ES OFF CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -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 1)
|
||||
set_if_not_defined(FSO_VERSION_MINOR 3)
|
||||
set_if_not_defined(FSO_VERSION_BUILD 0)
|
||||
|
||||
# A numeric revision e.g. the CI build number
|
||||
|
||||
@@ -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);
|
||||
ship_apply_global_damage(ship_objp, asteroid_objp, &asteroid_objp->pos, damage, asip->damage_type_idx, DamageSource::Explosion);
|
||||
weapon_area_apply_blast(nullptr, ship_objp, &asteroid_objp->pos, blast, false);
|
||||
} // end for
|
||||
}
|
||||
|
||||
@@ -1036,6 +1036,13 @@ 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;
|
||||
|
||||
@@ -594,6 +594,14 @@ 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)
|
||||
*/
|
||||
|
||||
+92
-17
@@ -33,7 +33,7 @@
|
||||
#include "parse/encrypt.h"
|
||||
#include "cfilesystem.h"
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
char Cfile_root_dir[CFILE_ROOT_DIRECTORY_LEN] = "";
|
||||
@@ -185,7 +185,12 @@ int cfile_init(const char *exe_dir, const char *cdrom_dir)
|
||||
|
||||
char buf[CFILE_ROOT_DIRECTORY_LEN];
|
||||
|
||||
strncpy(buf, exe_dir, CFILE_ROOT_DIRECTORY_LEN - 1);
|
||||
#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
|
||||
|
||||
buf[CFILE_ROOT_DIRECTORY_LEN - 1] = '\0';
|
||||
|
||||
@@ -1105,28 +1110,98 @@ void cfread_string(char *buf, int n, CFILE *file)
|
||||
} while (c != 0 );
|
||||
}
|
||||
|
||||
void cfread_string_len(char *buf, int buffer_size, CFILE *file)
|
||||
{
|
||||
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");
|
||||
}
|
||||
namespace {
|
||||
struct string_len_header {
|
||||
int len = 0;
|
||||
int stored = 0;
|
||||
int start = 0;
|
||||
bool length_cut = false;
|
||||
};
|
||||
|
||||
buf[MIN(len, buffer_size - 1)] = 0;
|
||||
// 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)
|
||||
{
|
||||
string_len_header h;
|
||||
if (!cf_is_valid(file)) {
|
||||
return h;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
SCP_string cfread_string_len(CFILE *file)
|
||||
{
|
||||
int len = cfread_int(file);
|
||||
string_len_header h = read_string_len_header(file);
|
||||
|
||||
SCP_string str;
|
||||
str.resize(len);
|
||||
|
||||
if (len)
|
||||
cfread(&str[0], len, 1, file);
|
||||
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);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
+8
-2
@@ -320,20 +320,26 @@ 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.
|
||||
*
|
||||
* 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)
|
||||
*
|
||||
* Keeps at most buffer_size - 1 characters and leaves the file just past the whole string, unless it throws.
|
||||
*
|
||||
* @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);
|
||||
|
||||
|
||||
+24
-14
@@ -193,21 +193,8 @@ int cfseek( CFILE *cfile, int offset, int where )
|
||||
}
|
||||
|
||||
|
||||
// cfread() reads from a file
|
||||
//
|
||||
// returns: returns the number of full elements read
|
||||
//
|
||||
//
|
||||
int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
|
||||
size_t cf_check_read_size(CFILE* cfile, size_t size)
|
||||
{
|
||||
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;
|
||||
@@ -226,6 +213,29 @@ int cfread(void *buf, int elsize, int nelem, CFILE *cfile)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -62,4 +62,9 @@ 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
|
||||
+85
-50
@@ -75,6 +75,7 @@ const char *cmdline_arg_types[] =
|
||||
enum class flag_output_type {
|
||||
Binary,
|
||||
Json_V1,
|
||||
Json_V2,
|
||||
};
|
||||
|
||||
// variables
|
||||
@@ -555,7 +556,12 @@ 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;
|
||||
@@ -1358,51 +1364,58 @@ 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();
|
||||
|
||||
{
|
||||
auto version_obj = json_object();
|
||||
json_add_version(root);
|
||||
json_add_easy_flags(root);
|
||||
json_add_flags(root);
|
||||
|
||||
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();
|
||||
|
||||
@@ -1513,6 +1526,19 @@ 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");
|
||||
|
||||
@@ -1570,6 +1596,8 @@ 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");
|
||||
@@ -1580,17 +1608,23 @@ static flag_output_type get_flags_output_type() {
|
||||
|
||||
static void write_flags() {
|
||||
auto type = get_flags_output_type();
|
||||
switch(type) {
|
||||
case flag_output_type::Binary:
|
||||
write_flags_file();
|
||||
break;
|
||||
case flag_output_type::Json_V1:
|
||||
json_t* root = json_get_v1();
|
||||
|
||||
json_dumpf(root, stdout, JSON_INDENT(4));
|
||||
json_decref(root);
|
||||
break;
|
||||
if (type == flag_output_type::Binary) {
|
||||
write_flags_file();
|
||||
return;
|
||||
}
|
||||
|
||||
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()
|
||||
@@ -1787,6 +1821,7 @@ 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( ) )
|
||||
|
||||
@@ -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.type == Preset_t::tbl) || (p.type == Preset_t::hardcode)));
|
||||
return ((p.name == str) && ((p.get_type() == Preset_t::tbl) || (p.get_type() == Preset_t::hardcode)));
|
||||
});
|
||||
|
||||
if (it != Control_config_presets.end()) {
|
||||
@@ -1353,15 +1353,14 @@ bool control_config_accept(bool API_Access)
|
||||
}
|
||||
|
||||
// Pack the current bindings into a preset, then save the file
|
||||
CC_preset preset;
|
||||
preset.name = std::move(str);
|
||||
CC_preset preset(std::move(str), Preset_t::pst);
|
||||
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.resize(1);
|
||||
Control_config_presets.erase(Control_config_presets.begin() + 1, Control_config_presets.end());
|
||||
load_preset_files();
|
||||
|
||||
// finally, save the new preset so that changes will get saved to this preset
|
||||
@@ -1778,7 +1777,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.type == Preset_t::tbl) || (p.type == Preset_t::hardcode));
|
||||
return (p.name == newName) && ((p.get_type() == Preset_t::tbl) || (p.get_type() == Preset_t::hardcode));
|
||||
});
|
||||
|
||||
if (it != Control_config_presets.end()) {
|
||||
@@ -1791,9 +1790,7 @@ 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;
|
||||
preset.name = newName;
|
||||
preset.type = Preset_t::pst;
|
||||
CC_preset preset(newName, Preset_t::pst);
|
||||
std::copy(Control_config.begin(), Control_config.end(), std::back_inserter(preset.bindings));
|
||||
|
||||
// Done with the file
|
||||
@@ -1805,7 +1802,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.resize(1);
|
||||
Control_config_presets.erase(Control_config_presets.begin() + 1, Control_config_presets.end());
|
||||
load_preset_files(newName);
|
||||
|
||||
// use the newly created preset
|
||||
@@ -1813,10 +1810,9 @@ bool control_config_create_new_preset(const SCP_string& newName, bool overwrite)
|
||||
|
||||
return true;
|
||||
|
||||
} else if ((clone->name != preset.name) || (clone->type != Preset_t::pst)) {
|
||||
} else if ((clone->name != preset.name) || (clone->get_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
|
||||
@@ -1826,7 +1822,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.type == Preset_t::tbl) || (p.type == Preset_t::hardcode));
|
||||
return (p.name == newName) && ((p.get_type() == Preset_t::tbl) || (p.get_type() == Preset_t::hardcode));
|
||||
});
|
||||
|
||||
if (it != Control_config_presets.end()) {
|
||||
@@ -1838,13 +1834,13 @@ bool control_config_clone_preset(const CC_preset& preset, const SCP_string& newN
|
||||
return false;
|
||||
}
|
||||
|
||||
CC_preset newPreset = preset;
|
||||
newPreset.name = newName;
|
||||
CC_preset newPreset(newName, Preset_t::pst);
|
||||
newPreset.bindings = preset.bindings;
|
||||
|
||||
bool success = save_preset_file(newPreset, false);
|
||||
|
||||
// Reload the presets from file.
|
||||
Control_config_presets.resize(1);
|
||||
Control_config_presets.erase(Control_config_presets.begin() + 1, Control_config_presets.end());
|
||||
load_preset_files(newName);
|
||||
|
||||
// use the newly cloned preset
|
||||
|
||||
@@ -572,20 +572,28 @@ 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;
|
||||
|
||||
public:
|
||||
CC_preset() = default;
|
||||
CC_preset(const CC_preset& A) = default;
|
||||
private:
|
||||
// No default value: the type is always set explicitly by the constructor
|
||||
Preset_t type;
|
||||
|
||||
CC_preset& operator=(const CC_preset&);
|
||||
/*!
|
||||
* Checks if the given preset is a duplicate of this one
|
||||
*/
|
||||
bool is_duplicate_of(CC_preset&);
|
||||
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;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
||||
@@ -292,10 +292,8 @@ void control_config_common_init_bindings() {
|
||||
// init default preset
|
||||
Control_config_presets.clear();
|
||||
|
||||
CC_preset preset;
|
||||
CC_preset preset("default", Preset_t::hardcode);
|
||||
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));
|
||||
@@ -1733,14 +1731,13 @@ 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;
|
||||
CC_preset new_preset("", Preset_t::tbl);
|
||||
|
||||
// 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
|
||||
@@ -3002,23 +2999,22 @@ CCI_builder& CCI_builder::operator()(IoActionId action_id, short primary, short
|
||||
return *this;
|
||||
}
|
||||
|
||||
CC_preset& CC_preset::operator=(const CC_preset& A) {
|
||||
name = A.name;
|
||||
type = A.type;
|
||||
|
||||
std::copy(A.bindings.begin(), A.bindings.end(), bindings.begin());
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
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]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Else, did not find any differences in the bindings. Thus, this preset is a duplicate
|
||||
return true;
|
||||
}
|
||||
@@ -74,9 +74,7 @@ void load_preset_files(SCP_string clone) {
|
||||
|
||||
|
||||
// Start reading in data
|
||||
CC_preset preset;
|
||||
preset.name = file;
|
||||
preset.type = Preset_t::pst;
|
||||
CC_preset preset(file, Preset_t::pst);
|
||||
preset.bindings.resize(Control_config.size());
|
||||
|
||||
size_t size;
|
||||
@@ -134,7 +132,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->type != Preset_t::pst)) {
|
||||
} else if ((it->name != preset.name) || ((it->get_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 {
|
||||
@@ -173,7 +171,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.resize(1);
|
||||
Control_config_presets.erase(Control_config_presets.begin() + 1, Control_config_presets.end());
|
||||
load_preset_files();
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
#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 {
|
||||
@@ -17,8 +21,6 @@ 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,3 +1,7 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
layout (location = 0) in vec4 vertPosition;
|
||||
layout (location = 1) in vec4 vertColor;
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
#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
|
||||
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
#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
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
#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;
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
#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
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
#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/
|
||||
@@ -29,9 +35,14 @@ 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
|
||||
out vec4 fragOut0; // Diffuse buffer
|
||||
out vec4 fragOut1; // Normal buffer
|
||||
out vec4 fragOut2; // Emissive buffer
|
||||
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
|
||||
|
||||
flat in mat4 invModelMatrix;
|
||||
flat in vec3 decalDirection;
|
||||
@@ -124,7 +135,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 - smoothstep(angle_fade_start, normal_angle_cutoff, angle));
|
||||
alpha = alpha * (1.0 - smoothstep(angle_fade_start, normal_angle_cutoff, angle));
|
||||
|
||||
return normal;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
// Binding 0: box vertex positions
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
#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
|
||||
@@ -20,22 +27,28 @@ 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;
|
||||
|
||||
layout(location = 0) out vec4 fragOut0;
|
||||
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
|
||||
layout(location = 2) in float vClipDist;
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
|
||||
in float vClipDist;
|
||||
#endif
|
||||
|
||||
uniform sampler2DArray baseMap;
|
||||
|
||||
in vec4 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
out vec4 fragOut0;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -43,6 +56,11 @@ out vec4 fragOut0;
|
||||
|
||||
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,3 +1,9 @@
|
||||
#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
|
||||
@@ -38,10 +44,15 @@ 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
|
||||
|
||||
out float gl_ClipDistance[1];
|
||||
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
|
||||
out float vClipDist;
|
||||
#endif
|
||||
|
||||
out vec4 fragTexCoord;
|
||||
in vec4 vertTexCoord;
|
||||
@@ -58,13 +69,16 @@ 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 {
|
||||
#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.
|
||||
#ifndef SDR_FLAG_SW_CLIP_FALLBACK
|
||||
gl_ClipDistance[0] = 1.0;
|
||||
#endif
|
||||
#else
|
||||
vClipDist = 1.0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
out vec4 fragOut0;
|
||||
out vec4 fragOut1;
|
||||
out vec4 fragOut2;
|
||||
out vec4 fragOut3;
|
||||
out vec4 fragOut4;
|
||||
out vec4 fragOut5;
|
||||
#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;
|
||||
|
||||
void main()
|
||||
{
|
||||
fragOut0 = vec4(0.0, 0.0, 0.0, 1.0); // color
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
//? #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"
|
||||
|
||||
#ifdef VULKAN
|
||||
#ifndef GL_ES
|
||||
layout(location = 0) out vec4 fragOut0;
|
||||
#else
|
||||
out vec4 fragOut0;
|
||||
layout(location = 5) out vec4 fragOut0; //COLOR_ATTACHMENTn is on location n in ES, here it uses COLOR_ATTACHMENT5
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
@@ -145,7 +153,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*dist)), 0.0, 1.0);
|
||||
float alpha_adjust = clamp(alpha + (sourceRadius/(2.0*dist)), 0.0, 1.0);
|
||||
area_normalisation = alpha/alpha_adjust;
|
||||
area_normalisation *= area_normalisation;
|
||||
//end chunk
|
||||
@@ -188,7 +196,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*dist)), 1.0);
|
||||
float alpha_adjust = min(alpha + (sourceRadius/(2.0*dist)), 1.0);
|
||||
area_normalisation = alpha/alpha_adjust;
|
||||
// don't need to square as it's a line rather than a sphere.
|
||||
//end chunk
|
||||
@@ -235,11 +243,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; // Ideally this would be #define'd and shader recompiled with envmap rez changes
|
||||
const float REZ_BIAS = log2(ENV_REZ * sqrt(3));
|
||||
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));
|
||||
|
||||
float alphaSqr = alpha * alpha;
|
||||
float rough_bias = 0.5 * log2(2/alphaSqr - 1);
|
||||
float rough_bias = 0.5 * log2(2.0/alphaSqr - 1.0);
|
||||
float mip_bias = REZ_BIAS - rough_bias;
|
||||
|
||||
// Sample light, using mip bias to blur it.
|
||||
@@ -257,7 +265,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);
|
||||
float NdotL = max(dot(light_dir, normal),0.0);
|
||||
|
||||
float g1vNL = GeometrySchlickGGX(NdotL, k);
|
||||
vec3 specEnvLighting = specEnvColour.rgb * fresnel * g1vNL;
|
||||
@@ -338,6 +346,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,3 +1,7 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
#endif
|
||||
|
||||
#include "lighting.sdr"
|
||||
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
#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;
|
||||
@@ -14,8 +20,6 @@ in vec4 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
in float fragOffset;
|
||||
|
||||
out vec4 fragOut0;
|
||||
|
||||
uniform sampler2DArray baseMap;
|
||||
uniform sampler2D depthMap;
|
||||
uniform sampler2D distMap;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
layout(location = 0) in vec4 vertPosition;
|
||||
layout(location = 1) in vec4 vertColor;
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
#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
|
||||
@@ -18,8 +24,6 @@ in vec4 fragPosition;
|
||||
in vec4 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
|
||||
out vec4 fragOut0;
|
||||
|
||||
uniform sampler2DArray baseMap;
|
||||
uniform sampler2D depthMap;
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
layout (points) in;
|
||||
layout (triangle_strip, max_vertices = 4) out;
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
layout(location = 0) in vec4 vertPosition;
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
precision highp samplerCube;
|
||||
#endif
|
||||
|
||||
uniform samplerCube envmap;
|
||||
|
||||
in vec4 fragTexCoord;
|
||||
|
||||
out vec4 fragOut0;
|
||||
|
||||
layout (location = 0) out vec4 outColor;
|
||||
layout(location=0) out vec4 fragOut0;
|
||||
|
||||
void main() {
|
||||
const float theta = -fragTexCoord.x * (2.0 * M_PI);
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
#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
|
||||
@@ -40,7 +43,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 * depth_val - 1;
|
||||
float depth_normalized = 2.0 * depth_val - 1.0;
|
||||
// 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));
|
||||
|
||||
@@ -50,7 +53,7 @@ void main()
|
||||
if (isinf(view_depth)) {
|
||||
fragOut0.rgb = color_in.rgb;
|
||||
} else {
|
||||
float fog_dist = clamp(1 - pow(fog_density, view_depth - fog_start) , 0.0, 1.0);
|
||||
float fog_dist = clamp(1.0 - 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,3 +1,12 @@
|
||||
#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
|
||||
@@ -500,8 +509,12 @@ 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;
|
||||
out vec4 fragOut0;
|
||||
#endif
|
||||
layout(location=0)out vec4 fragOut0;
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
layout(location = 0) out vec2 fragTexCoord;
|
||||
@@ -10,7 +13,11 @@ 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;
|
||||
|
||||
@@ -1,6 +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;
|
||||
// [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];
|
||||
@@ -8,9 +14,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,12 +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;
|
||||
#else
|
||||
in vec4 fragTexCoord;
|
||||
out vec4 fragOut0;
|
||||
uniform sampler2D tex;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
const float SRGB_GAMMA = 2.2;
|
||||
const float SRGB_GAMMA_INVERSE = 1.0 / SRGB_GAMMA;
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
//!#version 150
|
||||
#include "gamma.sdr" //! #include "gamma.sdr"
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
precision highp samplerCube;
|
||||
#endif
|
||||
|
||||
#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
|
||||
@@ -154,8 +160,8 @@ vec3( 0.7305, 0.6698, 0.1333)
|
||||
);
|
||||
|
||||
void main() {
|
||||
float xpos = 2 * gl_FragCoord.x/16 - 1; // remap to (-1, 1)
|
||||
float ypos = 2 * gl_FragCoord.y/16 - 1;
|
||||
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;
|
||||
vec3 fragDir;
|
||||
vec4 fragCol = vec4(0.0);
|
||||
|
||||
@@ -191,5 +197,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));
|
||||
fragOut0 = linear_to_srgb(PI * fragCol * (1.0/128.0));
|
||||
}
|
||||
@@ -57,7 +57,7 @@ vec3 ComputeGGX(vec3 specColor, vec3 diffColor, vec3 light, vec3 normal, vec3 ha
|
||||
|
||||
|
||||
|
||||
vec3 specular = distribution * fresnel * geometry / (4*dotNV*dotNL + 0.0001);
|
||||
vec3 specular = distribution * fresnel * geometry / (4.0*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, fresnelFactor, dotNL);
|
||||
return SpecularBlinnPhong(specColor, light, normal, halfVec, 32.0, fresnelFactor, dotNL);
|
||||
#else
|
||||
return ComputeGGX(specColor, diffColor, light, normal, halfVec, view, roughness, fresnelFactor, dotNL);
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
#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]
|
||||
@@ -9,8 +16,6 @@ layout(set = 1, binding = 1) uniform sampler2D textures[16];
|
||||
#else
|
||||
in vec4 fragTexCoord;
|
||||
|
||||
out vec4 fragOut0;
|
||||
|
||||
uniform sampler2D scene;
|
||||
uniform sampler2D cockpit;
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
//? #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"
|
||||
@@ -112,6 +121,13 @@ 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]
|
||||
@@ -139,13 +155,12 @@ 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;
|
||||
@@ -185,13 +200,10 @@ 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)
|
||||
@@ -283,6 +295,10 @@ 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;
|
||||
|
||||
@@ -464,7 +480,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 - pow(fogDensity, vertIn.fogDist) , 0.0, 1.0);
|
||||
float fogDensityFinal = clamp(1.0 - 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;
|
||||
@@ -509,11 +525,12 @@ 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;
|
||||
glossData = 0.0f;
|
||||
#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;
|
||||
|
||||
aoFactors.x = 0.0f;
|
||||
// and leave nothing for the light pass to add on top of it
|
||||
baseColor.rgb = vec3(0.0);
|
||||
specColor = vec4(0.0);
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
#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"
|
||||
@@ -100,6 +107,9 @@ 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 {
|
||||
@@ -117,6 +127,10 @@ 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
|
||||
@@ -166,8 +180,19 @@ out VertexOutput {
|
||||
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_SHADOWS
|
||||
|
||||
if (use_clip_plane != 0) {
|
||||
gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[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
|
||||
}
|
||||
|
||||
EmitVertex();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
|
||||
#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"
|
||||
@@ -145,6 +155,10 @@ 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
|
||||
@@ -166,6 +180,10 @@ 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
|
||||
@@ -247,12 +265,20 @@ void main()
|
||||
}
|
||||
#prereplace ENDIF_FLAG //MODEL_SDR_FLAG_TRANSFORM
|
||||
|
||||
// Clip plane
|
||||
if(use_clip_plane != 0) {
|
||||
gl_ClipDistance[0] = dot(clip_equation, modelMatrix * orient * vertex);
|
||||
} else {
|
||||
gl_ClipDistance[0] = 1.0;
|
||||
}
|
||||
#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
|
||||
|
||||
|
||||
vertOut.position = position;
|
||||
vertOut.normal = normal;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "z-compress.sdr"
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
precision highp sampler2DMS;
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
layout(location = 0) in vec2 fragTexCoord;
|
||||
#include "z-compress.sdr"
|
||||
|
||||
layout(location = 0) out vec4 fragOut0;
|
||||
layout(location = 1) out vec4 fragOut1;
|
||||
@@ -9,6 +12,9 @@ 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]
|
||||
@@ -23,12 +29,7 @@ layout(std140, set = 2, binding = 0) uniform genericData {
|
||||
};
|
||||
#else
|
||||
in vec4 fragTexCoord;
|
||||
out vec4 fragOut0;
|
||||
out vec4 fragOut1;
|
||||
out vec4 fragOut2;
|
||||
out vec4 fragOut3;
|
||||
out vec4 fragOut4;
|
||||
out vec4 fragOut5;
|
||||
layout(location=5)out vec4 fragOut5;
|
||||
|
||||
uniform sampler2DMS texColor;
|
||||
uniform sampler2DMS texPos;
|
||||
@@ -176,7 +177,7 @@ float getMedianDist(ivec2 texel) {
|
||||
|
||||
//Fallback, do front first
|
||||
float getMedianDist(ivec2 texel) {
|
||||
float minDist = -1000000;
|
||||
float minDist = -1000000.0;
|
||||
for(int i = 0; i < samples; i++) {
|
||||
minDist = max(minDist, uncompress_depth_value(texelFetch(texPos, texel, i).z));
|
||||
}
|
||||
@@ -190,7 +191,7 @@ float getMedianDist(ivec2 texel) {
|
||||
|
||||
void main()
|
||||
{
|
||||
vec2 texSize = textureSize(texColor);
|
||||
vec2 texSize = vec2(textureSize(texColor));
|
||||
#ifdef VULKAN
|
||||
ivec2 texel = ivec2(texSize * fragTexCoord);
|
||||
#else
|
||||
@@ -206,7 +207,7 @@ void main()
|
||||
vec4 normal = vec4(0);
|
||||
vec4 specular = vec4(0);
|
||||
vec4 emissive = vec4(0);
|
||||
float depth = 0;
|
||||
float depth = 0.0;
|
||||
|
||||
for(int i = 0; i < SAMPLE_CNT; i++) {
|
||||
vec4 localPos = texelFetch(texPos, texel, i);
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
precision highp sampler2DArray;
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
layout (location = 0) in vec2 ftcoord;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
layout (location = 0) in vec4 vertPosition;
|
||||
|
||||
@@ -7,6 +7,6 @@
|
||||
vec3 unpackNormal(vec2 normal_sample) {
|
||||
vec3 normal;
|
||||
normal.xy = normal_sample * 2.0 - 1.0;
|
||||
normal.z = sqrt(1 - normal_sample.x * normal_sample.x - normal_sample.y * normal_sample.y);
|
||||
normal.z = sqrt(1.0 - normal_sample.x * normal_sample.x - normal_sample.y * normal_sample.y);
|
||||
return normal;
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
precision highp sampler2D;
|
||||
#endif
|
||||
|
||||
#include "gamma.sdr"
|
||||
|
||||
in vec4 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
out vec4 fragOut0;
|
||||
layout(location=0) out vec4 fragOut0;
|
||||
|
||||
uniform sampler2D baseMap;
|
||||
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
#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
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
#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
|
||||
@@ -12,8 +19,6 @@ in vec2 fragTexCoord;
|
||||
in vec4 fragColor;
|
||||
in vec2 fragScreenPosition;
|
||||
|
||||
out vec4 fragOut0;
|
||||
|
||||
uniform sampler2DArray baseMap;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
layout(location = 0) in vec2 vertPosition;
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
//? #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
|
||||
}
|
||||
@@ -1,9 +1,16 @@
|
||||
//? #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;
|
||||
#ifdef GL_ARB_gpu_shader5
|
||||
#if defined(GL_ARB_gpu_shader5) || defined(GL_EXT_gpu_shader5)
|
||||
layout(invocations = NUM_SHADOW_CASCADES) in;
|
||||
#endif
|
||||
|
||||
@@ -24,16 +31,25 @@ layout (std140) uniform shadowCascadeParams {
|
||||
};
|
||||
|
||||
in VertexOutput {
|
||||
#if !defined(GL_ARB_gpu_shader5)
|
||||
#if !defined(GL_ARB_gpu_shader5) && !defined(GL_EXT_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)
|
||||
{
|
||||
#ifdef GL_ARB_gpu_shader5
|
||||
#if defined(GL_ARB_gpu_shader5) || defined(GL_EXT_gpu_shader5)
|
||||
if (gl_InvocationID >= cascade_count) return;
|
||||
int cascade_id = cascade_offset + gl_InvocationID;
|
||||
#else
|
||||
@@ -49,7 +65,12 @@ void main(void)
|
||||
}
|
||||
|
||||
gl_Layer = cascade_id;
|
||||
gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0];
|
||||
|
||||
#ifdef SDR_FLAG_SW_CLIP_FALLBACK
|
||||
vertOut.vClipDist = vertIn[vert].vClipDist;
|
||||
#else
|
||||
gl_ClipDistance[0] = gl_in[vert].gl_ClipDistance[0];
|
||||
#endif
|
||||
|
||||
EmitVertex();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
//? #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;
|
||||
@@ -66,12 +73,25 @@ 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)
|
||||
#if !defined(GL_ARB_gpu_shader5) && !defined(GL_EXT_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
|
||||
@@ -112,7 +132,7 @@ void main()
|
||||
#ifdef VULKAN
|
||||
int instanceIndex = gl_InstanceIndex;
|
||||
#else
|
||||
#if !defined(GL_ARB_gpu_shader5)
|
||||
#if !defined(GL_ARB_gpu_shader5) && !defined(GL_EXT_gpu_shader5)
|
||||
#ifdef APPLE
|
||||
vertOut.instance = float(gl_InstanceIDARB);
|
||||
#else
|
||||
@@ -133,11 +153,19 @@ 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,21 +1,22 @@
|
||||
#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,3 +1,7 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
precision highp int;
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
layout(location = 0) in vec4 vertPosition;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
precision highp sampler2D;
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
layout(std140, set = 2, binding = 0) uniform genericData {
|
||||
@@ -16,13 +20,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]
|
||||
@@ -33,8 +37,6 @@ in vec2 fragTexCoord;
|
||||
in vec2 fragPixcoord;
|
||||
in vec4 fragOffset[3];
|
||||
|
||||
out vec4 fragOut0;
|
||||
|
||||
uniform sampler2D edgesTex;
|
||||
uniform sampler2D areaTex;
|
||||
uniform sampler2D searchTex;
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
precision highp sampler2D;
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
layout(std140, set = 2, binding = 0) uniform genericData {
|
||||
@@ -16,19 +20,17 @@ 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,3 +1,7 @@
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
precision highp sampler2D;
|
||||
#endif
|
||||
|
||||
#ifdef VULKAN
|
||||
layout(std140, set = 2, binding = 0) uniform genericData {
|
||||
@@ -16,11 +20,12 @@ 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];
|
||||
@@ -30,8 +35,6 @@ 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,10 +1,15 @@
|
||||
#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,13 +1,18 @@
|
||||
#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
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
#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]
|
||||
@@ -8,7 +14,6 @@ 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;
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
#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]
|
||||
@@ -10,7 +17,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;
|
||||
@@ -77,7 +84,7 @@ void main()
|
||||
#else
|
||||
vec4 eyeDirection = p_inv * vec4(fragTexCoord.xy * 2.0 - 1.0, -1, 1);
|
||||
#endif
|
||||
eyeDirection.w = 0;
|
||||
eyeDirection.w = 0.0;
|
||||
vec3 rayDirection = normalize((v_inv * eyeDirection).xyz);
|
||||
|
||||
vec4 color_in = texture(composite, fragTexCoord.xy);
|
||||
@@ -103,14 +110,14 @@ void main()
|
||||
#endif
|
||||
|
||||
//The t at which the ray enters the nebula cube
|
||||
float maxtMin = max(0,max(tMin.x, max(tMin.y, tMin.z)));
|
||||
float maxtMin = max(0.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;
|
||||
float cumOMAlpha = 1.0;
|
||||
//The cumulative distance already travelled through the nebula.
|
||||
float cumnebdist = 0;
|
||||
float cumnebdist = 0.0;
|
||||
//The color of the current fragment
|
||||
vec3 cumcolor = vec3(0, 0, 0);
|
||||
|
||||
@@ -121,6 +128,14 @@ 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;
|
||||
@@ -159,7 +174,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, 1, (textureGrad(noise_volume_tex, position / noiseColorScale1, gradX, gradY).r + textureGrad(noise_volume_tex, position / noiseColorScale2, gradX, gradY).g) / 2.0 * noiseIntensity));
|
||||
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));
|
||||
#else
|
||||
vec3 stepcolor_neb = nebulaColor;
|
||||
#endif
|
||||
@@ -169,19 +184,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 * (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);
|
||||
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);
|
||||
directionalLightDepth += dlstepalpha * directionalLightStep;
|
||||
}
|
||||
stepcolor_diffuse *= beer_powder_norm * (1 - exp(-directionalLightDepth * 2.0)) * exp(-directionalLightDepth);
|
||||
stepcolor_diffuse *= beer_powder_norm * (1.0 - exp(-directionalLightDepth * 2.0)) * exp(-directionalLightDepth);
|
||||
|
||||
//Emissive
|
||||
cumnebdist += stepcolor_alpha * stepsize_current;
|
||||
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);
|
||||
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);
|
||||
|
||||
//Step finish
|
||||
vec3 stepcolor = clamp(stepcolor_diffuse + stepcolor_emissive, 0, 1);
|
||||
vec3 stepcolor = clamp(stepcolor_diffuse + stepcolor_emissive, 0.0, 1.0);
|
||||
cumcolor += stepalpha * cumOMAlpha * stepcolor;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,23 @@ 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!
|
||||
|
||||
@@ -289,7 +289,11 @@ 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)
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#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
|
||||
@@ -48,6 +49,7 @@
|
||||
#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
|
||||
@@ -234,9 +236,7 @@ inline void glDrawBuffer(GLenum buf)
|
||||
}
|
||||
}
|
||||
|
||||
// does not exist on ES, ES uses layout(location=) directly on shader
|
||||
// Notes:
|
||||
// -This function is not required to run.
|
||||
// ES uses layout(location = ) directly on shader
|
||||
#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,6 +254,7 @@ static inline void glQueryCounter(GLuint id, GLenum target)
|
||||
#else
|
||||
(void)id;
|
||||
#endif
|
||||
(void)target;
|
||||
}
|
||||
|
||||
// glGetCompressedTexImage not present on GLES and no equivalent
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
#if !defined __APPLE_CC__ && defined SCP_UNIX
|
||||
#if !defined __APPLE_CC__ && !defined __ANDROID__ && defined SCP_UNIX
|
||||
#include<glad/glad_glx.h>
|
||||
//Required because X defines none and always, which is used later
|
||||
#undef None
|
||||
@@ -1394,7 +1394,7 @@ bool gr_opengl_init(std::unique_ptr<os::GraphicsOperations>&& graphicsOps)
|
||||
Error(LOCATION, "Failed to load OpenGL!");
|
||||
}
|
||||
|
||||
#if !defined __APPLE_CC__ && defined SCP_UNIX
|
||||
#if !defined __APPLE_CC__ && !defined __ANDROID__ && defined SCP_UNIX
|
||||
if (!gladLoadGLXLoader(GL_context->getLoaderFunction(), nullptr, 0)) {
|
||||
Error(LOCATION, "Failed to load GLX!");
|
||||
}
|
||||
@@ -1453,7 +1453,9 @@ 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")));
|
||||
|
||||
@@ -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_HWND_POINTER,
|
||||
SDL_PROP_WINDOW_WIN32_HDC_POINTER,
|
||||
nullptr));
|
||||
|
||||
XrGraphicsBindingOpenGLWin32KHR graphicsBinding{
|
||||
|
||||
@@ -178,10 +178,20 @@ 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();
|
||||
|
||||
|
||||
@@ -360,6 +360,13 @@ 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);
|
||||
|
||||
@@ -732,7 +732,13 @@ 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;
|
||||
@@ -753,7 +759,13 @@ 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();
|
||||
@@ -1136,6 +1148,12 @@ 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;
|
||||
|
||||
|
||||
@@ -162,6 +162,19 @@ 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) {
|
||||
|
||||
@@ -55,6 +55,14 @@ 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);
|
||||
|
||||
|
||||
@@ -959,6 +959,7 @@ 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;
|
||||
|
||||
@@ -748,7 +748,7 @@ void VulkanDrawManager::renderShadowDraw(gr_buffer_handle ubo_handle, size_t ubo
|
||||
}
|
||||
|
||||
stateTracker->bindPipeline(pipeline, pipelineManager->getPipelineLayout());
|
||||
stateTracker->setDepthBias(-1024.0f, 0.0f);
|
||||
stateTracker->setDepthBias(1024.0f, 1.0f); // GL shadow pass: gr_zbias(-1024) = polygon offset factor 1, units 1024
|
||||
|
||||
// Bind descriptor sets: shadowMapData (per-draw) and shadowCascadeParams
|
||||
// (bound separately, per-frame, via shadow_cascade_params_bind) both live in
|
||||
@@ -1283,6 +1283,7 @@ 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);
|
||||
@@ -1483,9 +1484,6 @@ 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;
|
||||
}
|
||||
|
||||
@@ -1543,7 +1541,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 these AFTER bindPipeline,
|
||||
// before issuing the draw command. applyMaterial sets some of these AFTER bindPipeline,
|
||||
// so they may be dirty even when the pipeline didn't change.
|
||||
stateTracker->applyDynamicState();
|
||||
|
||||
|
||||
@@ -98,8 +98,7 @@ 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));
|
||||
|
||||
// Model matrix (identity for now, material doesn't provide one)
|
||||
vm_matrix4_set_identity(&data->modelMatrix);
|
||||
data->modelMatrix = gr_model_matrix_stack.get_transform();
|
||||
} else {
|
||||
data->clipEnabled = 0;
|
||||
vm_matrix4_set_identity(&data->modelMatrix);
|
||||
@@ -613,6 +612,9 @@ 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;
|
||||
@@ -638,9 +640,6 @@ 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;
|
||||
@@ -711,6 +710,9 @@ 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) {
|
||||
@@ -761,7 +763,8 @@ void vulkan_calculate_irrmap()
|
||||
writer.writeSet(materialSet, VulkanDescriptorManager::getSetTemplate(DescriptorSetIndex::Material));
|
||||
{
|
||||
std::array<vk::DescriptorImageInfo, VulkanDescriptorManager::MAX_TEXTURE_BINDINGS> texImages;
|
||||
texImages.fill(descManager->getFallbacks().texture2D);
|
||||
// irrmap-f.sdr declares this binding as samplerCube, so every element needs a cube view
|
||||
texImages.fill(descManager->getFallbacks().textureCube);
|
||||
texImages[0].imageView = envmapView;
|
||||
writer.setImageArray(MaterialBinding::TextureArray, texImages);
|
||||
}
|
||||
|
||||
@@ -121,6 +121,12 @@ public:
|
||||
*/
|
||||
vk::Pipeline getPipeline(const PipelineConfig& config, const vertex_layout& vertexLayout);
|
||||
|
||||
/**
|
||||
* @brief Destroy all cached pipelines; later getPipeline calls build them again
|
||||
* @note No submitted or recording command buffer may still use them
|
||||
*/
|
||||
void forgetAllPipelines() { m_pipelines.clear(); }
|
||||
|
||||
/**
|
||||
* @brief Get the common pipeline layout
|
||||
*
|
||||
|
||||
@@ -272,8 +272,7 @@ vk::DescriptorImageInfo VulkanDistortion::getTextureInfo() const
|
||||
// Distortion_texture[!Distortion_switch] binding for thrusters).
|
||||
// After update() toggles the switch, m_switch points to the old read
|
||||
// source, which is the texture that was just written.
|
||||
return {m_ctx->linearSampler, m_tex[m_switch].view,
|
||||
vk::ImageLayout::eShaderReadOnlyOptimal};
|
||||
return {m_sampler, m_tex[m_switch].view, vk::ImageLayout::eShaderReadOnlyOptimal};
|
||||
}
|
||||
|
||||
} // namespace graphics::vulkan
|
||||
@@ -531,6 +531,9 @@ void VulkanLDR::executeLightshafts(vk::CommandBuffer cmd)
|
||||
if (dot > 0.7f) {
|
||||
sun_x = asinf_safe(vm_vec_dot(&light_dir, &Eye_matrix.vec.rvec)) / PI * 1.5f + 0.5f;
|
||||
sun_y = asinf_safe(vm_vec_dot(&light_dir, &Eye_matrix.vec.uvec)) / PI * 1.5f * gr_screen.clip_aspect + 0.5f;
|
||||
// The formula above is OpenGL's, whose texture V grows upward. The Vulkan fullscreen triangle in
|
||||
// post-v.sdr has V = 0 at the top of the screen, so flip to the space ls-f.sdr's fragTexCoord is in.
|
||||
sun_y = 1.0f - sun_y;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -486,6 +486,7 @@ void VulkanDeferredLighting::render(vk::CommandBuffer cmd)
|
||||
tcache_slot_vulkan* envMapSlot = nullptr;
|
||||
tcache_slot_vulkan* irrMapSlot = nullptr;
|
||||
if (envMapAvailable) {
|
||||
texMgr->ensureCubemapUploaded(ENVMAP);
|
||||
envMapSlot = texMgr->getTextureSlot(ENVMAP);
|
||||
if (!envMapSlot || !envMapSlot->imageView || !envMapSlot->isCubemap) {
|
||||
envMapAvailable = false;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "VulkanRaytracing.h"
|
||||
|
||||
#include "VulkanDeletionQueue.h"
|
||||
|
||||
namespace graphics::vulkan {
|
||||
|
||||
namespace {
|
||||
@@ -222,17 +224,36 @@ uint64_t VulkanRaytracingManager::makeKey(int pm_id, int submodel_num)
|
||||
|
||||
void VulkanRaytracingManager::destroyBlasEntry(BlasEntry& entry)
|
||||
{
|
||||
entry.accelStruct.reset();
|
||||
// TLASes in frames still in flight hold this BLAS's device address, so defer like ensureTlasCapacity does
|
||||
if (entry.accelStruct) {
|
||||
getDeletionQueue()->queueAccelerationStructure(entry.accelStruct.release());
|
||||
}
|
||||
|
||||
if (entry.buffer) {
|
||||
m_device.destroyBuffer(entry.buffer);
|
||||
getDeletionQueue()->queueBuffer(entry.buffer, entry.allocation);
|
||||
entry.buffer = nullptr;
|
||||
}
|
||||
if (entry.allocation.isValid()) {
|
||||
entry.allocation = VulkanAllocation();
|
||||
} else if (entry.allocation.isValid()) {
|
||||
m_memoryManager->freeAllocation(entry.allocation);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanRaytracingManager::retireBuiltTlases()
|
||||
{
|
||||
// a built TLAS may still point at a retired BLAS, and screens that never rebuild the TLAS would keep binding it
|
||||
for (auto& frame : m_frameTlas) {
|
||||
if (frame.tlas) {
|
||||
getDeletionQueue()->queueAccelerationStructure(frame.tlas.release());
|
||||
}
|
||||
if (frame.tlasBuffer) {
|
||||
getDeletionQueue()->queueBuffer(frame.tlasBuffer, frame.tlasAllocation);
|
||||
frame.tlasBuffer = nullptr;
|
||||
frame.tlasAllocation = VulkanAllocation();
|
||||
}
|
||||
frame.tlasCapacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
vk::CommandBuffer VulkanRaytracingManager::beginSingleTimeCommands()
|
||||
{
|
||||
vk::CommandBufferAllocateInfo allocInfo;
|
||||
|
||||
@@ -93,8 +93,9 @@ public:
|
||||
/**
|
||||
* @brief Purge all cached BLASes for a model and free their GPU resources.
|
||||
* Called from model_unload()/model_free_all() before the model is destroyed.
|
||||
* @return true if any BLAS was retired (the shadow TLAS binding must then be refreshed)
|
||||
*/
|
||||
void onModelUnloaded(int pm_id);
|
||||
bool onModelUnloaded(int pm_id);
|
||||
|
||||
/**
|
||||
* @brief Rebuild the top-level acceleration structure for this frame from
|
||||
@@ -172,6 +173,7 @@ private:
|
||||
const BlasEntry* getOrBuildBlasEntry(int pm_id, int submodel_num);
|
||||
|
||||
void destroyBlasEntry(BlasEntry& entry);
|
||||
void retireBuiltTlases();
|
||||
|
||||
bool createRawBuffer(vk::DeviceSize size,
|
||||
vk::BufferUsageFlags usage,
|
||||
|
||||
@@ -182,6 +182,7 @@ const VulkanRaytracingManager::BlasEntry* VulkanRaytracingManager::getOrBuildBla
|
||||
// address) -- destroy and fall through to rebuild.
|
||||
destroyBlasEntry(it->second);
|
||||
m_blasCache.erase(it);
|
||||
retireBuiltTlases();
|
||||
}
|
||||
|
||||
BlasEntry entry;
|
||||
@@ -236,20 +237,27 @@ void VulkanRaytracingManager::onModelLoaded(int pm_id)
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanRaytracingManager::onModelUnloaded(int pm_id)
|
||||
bool VulkanRaytracingManager::onModelUnloaded(int pm_id)
|
||||
{
|
||||
if (!m_enabled) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool retired = false;
|
||||
for (auto it = m_blasCache.begin(); it != m_blasCache.end();) {
|
||||
if (static_cast<int>(it->first >> 32) == pm_id) {
|
||||
destroyBlasEntry(it->second);
|
||||
it = m_blasCache.erase(it);
|
||||
retired = true;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (retired) {
|
||||
retireBuiltTlases();
|
||||
}
|
||||
return retired;
|
||||
}
|
||||
|
||||
} // namespace graphics::vulkan
|
||||
@@ -10,7 +10,6 @@ VulkanRenderFrame::VulkanRenderFrame(vk::Device device, vk::SwapchainKHR swapCha
|
||||
constexpr vk::FenceCreateInfo fenceCreateInfo;
|
||||
|
||||
m_imageAvailableSemaphore = device.createSemaphoreUnique(semaphoreCreateInfo);
|
||||
m_renderingFinishedSemaphore = device.createSemaphoreUnique(semaphoreCreateInfo);
|
||||
m_frameInFlightFence = device.createFenceUnique(fenceCreateInfo);
|
||||
}
|
||||
bool VulkanRenderFrame::waitForFinish(uint64_t timeoutNs)
|
||||
@@ -82,7 +81,8 @@ SwapChainStatus VulkanRenderFrame::acquireSwapchainImage(uint32_t& outImageIndex
|
||||
}
|
||||
return SwapChainStatus::eSuccess;
|
||||
}
|
||||
SwapChainStatus VulkanRenderFrame::submitAndPresent(const SCP_vector<vk::CommandBuffer>& cmdBuffers)
|
||||
SwapChainStatus VulkanRenderFrame::submitAndPresent(const SCP_vector<vk::CommandBuffer>& cmdBuffers,
|
||||
vk::Semaphore renderingFinished)
|
||||
{
|
||||
Assertion(!m_inFlight, "Cannot submit a frame for presentation when it is still in flight.");
|
||||
|
||||
@@ -99,7 +99,7 @@ SwapChainStatus VulkanRenderFrame::submitAndPresent(const SCP_vector<vk::Command
|
||||
submitInfo.commandBufferCount = static_cast<uint32_t>(cmdBuffers.size());
|
||||
submitInfo.pCommandBuffers = cmdBuffers.data();
|
||||
|
||||
const std::array<vk::Semaphore, 1> signalSemaphores = {m_renderingFinishedSemaphore.get()};
|
||||
const std::array<vk::Semaphore, 1> signalSemaphores = {renderingFinished};
|
||||
submitInfo.signalSemaphoreCount = 1;
|
||||
submitInfo.pSignalSemaphores = signalSemaphores.data();
|
||||
|
||||
@@ -147,7 +147,6 @@ void VulkanRenderFrame::recreateSyncObjects()
|
||||
|
||||
constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo;
|
||||
m_imageAvailableSemaphore = m_device.createSemaphoreUnique(semaphoreCreateInfo);
|
||||
m_renderingFinishedSemaphore = m_device.createSemaphoreUnique(semaphoreCreateInfo);
|
||||
}
|
||||
|
||||
} // namespace graphics::vulkan
|
||||
@@ -30,7 +30,7 @@ class VulkanRenderFrame {
|
||||
|
||||
void onFrameFinished(std::function<void()> finishFunc);
|
||||
|
||||
SwapChainStatus submitAndPresent(const SCP_vector<vk::CommandBuffer>& cmdBuffers);
|
||||
SwapChainStatus submitAndPresent(const SCP_vector<vk::CommandBuffer>& cmdBuffers, vk::Semaphore renderingFinished);
|
||||
|
||||
void updateSwapChain(vk::SwapchainKHR swapChain);
|
||||
|
||||
@@ -50,7 +50,6 @@ class VulkanRenderFrame {
|
||||
vk::Queue m_presentQueue;
|
||||
|
||||
vk::UniqueSemaphore m_imageAvailableSemaphore;
|
||||
vk::UniqueSemaphore m_renderingFinishedSemaphore;
|
||||
vk::UniqueFence m_frameInFlightFence;
|
||||
SCP_vector<std::function<void()>> m_frameFinishedCallbacks;
|
||||
|
||||
|
||||
@@ -430,6 +430,15 @@ void VulkanRenderer::createPresentSyncObjects()
|
||||
}
|
||||
|
||||
m_swapChainImageRenderImage.resize(m_swapChainImages.size(), nullptr);
|
||||
createRenderingFinishedSemaphores();
|
||||
}
|
||||
void VulkanRenderer::createRenderingFinishedSemaphores()
|
||||
{
|
||||
constexpr vk::SemaphoreCreateInfo semaphoreCreateInfo;
|
||||
m_renderingFinishedSemaphores.clear();
|
||||
for (size_t i = 0; i < m_swapChainImages.size(); ++i) {
|
||||
m_renderingFinishedSemaphores.push_back(m_device->createSemaphoreUnique(semaphoreCreateInfo));
|
||||
}
|
||||
}
|
||||
|
||||
bool VulkanRenderer::readbackFramebuffer(ubyte** outPixels, uint32_t* outWidth, uint32_t* outHeight)
|
||||
|
||||
@@ -95,6 +95,12 @@ class VulkanRenderer {
|
||||
*/
|
||||
void setupFrame();
|
||||
|
||||
/**
|
||||
* @brief Drop every cached pipeline at the start of the next frame; each is rebuilt on its next use
|
||||
* Called after the shaders are recompiled, since a pipeline keeps the shader code it was built from
|
||||
*/
|
||||
void requestPipelineRebuild() { m_pipelinesStale = true; }
|
||||
|
||||
/**
|
||||
* @brief End frame - ends render pass, submits, and presents
|
||||
* Called at the END of each frame after all draw calls
|
||||
@@ -451,10 +457,12 @@ class VulkanRenderer {
|
||||
void createCommandPool(const PhysicalDeviceValues& values);
|
||||
|
||||
void createPresentSyncObjects();
|
||||
void createRenderingFinishedSemaphores();
|
||||
|
||||
void acquireNextSwapChainImage();
|
||||
|
||||
bool recreateSwapChain();
|
||||
bool shouldRecreateSwapChain(SwapChainStatus status) const;
|
||||
|
||||
void createImGuiDescriptorPool();
|
||||
void initImGui();
|
||||
@@ -479,6 +487,7 @@ class VulkanRenderer {
|
||||
bool m_hdrActive = false; // True when an HDR10 (PQ/BT.2020) swap chain was negotiated
|
||||
bool m_hdrMetadataSupported = false; // VK_EXT_hdr_metadata device extension enabled
|
||||
vk::Extent2D m_swapChainExtent;
|
||||
vk::SurfaceTransformFlagBitsKHR m_swapChainPreTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
|
||||
// The resolution the engine draws at (gr_screen.max_w/max_h), which is not
|
||||
// always the window size: when Cmdline_window_res is set,
|
||||
// SDLGraphicsOperations::createViewport sizes the window from that instead,
|
||||
@@ -491,6 +500,8 @@ class VulkanRenderer {
|
||||
SCP_vector<vk::UniqueImageView> m_swapChainImageViews;
|
||||
SCP_vector<vk::UniqueFramebuffer> m_swapChainFramebuffers;
|
||||
SCP_vector<VulkanRenderFrame*> m_swapChainImageRenderImage;
|
||||
// per image, not per frame: a frame's fence does not tell us when present has consumed the semaphore
|
||||
SCP_vector<vk::UniqueSemaphore> m_renderingFinishedSemaphores;
|
||||
|
||||
// HDR composition pipeline: the whole frame is rendered into these fp16
|
||||
// images (via m_renderPass / m_swapChainFramebuffers) instead of directly
|
||||
@@ -528,6 +539,7 @@ class VulkanRenderer {
|
||||
vk::CommandBuffer m_currentCommandBuffer;
|
||||
SCP_vector<vk::CommandBuffer> m_currentCommandBuffers; // For cleanup
|
||||
bool m_frameInProgress = false;
|
||||
bool m_pipelinesStale = false;
|
||||
|
||||
// Swap chain recreation
|
||||
bool m_swapChainNeedsRecreation = false;
|
||||
|
||||
@@ -81,7 +81,7 @@ void VulkanRenderer::acquireNextSwapChainImage()
|
||||
MAX_ACQUIRE_ATTEMPTS);
|
||||
}
|
||||
|
||||
if (status == SwapChainStatus::eSuboptimal) {
|
||||
if (shouldRecreateSwapChain(status)) {
|
||||
m_swapChainNeedsRecreation = true;
|
||||
}
|
||||
|
||||
@@ -101,6 +101,14 @@ void VulkanRenderer::setupFrame()
|
||||
return;
|
||||
}
|
||||
|
||||
// before recording: the recompile can run mid-frame, while the frame being recorded still holds old pipelines
|
||||
if (m_pipelinesStale) {
|
||||
mprintf(("Vulkan: shaders were recompiled, rebuilding pipelines\n"));
|
||||
m_device->waitIdle();
|
||||
m_pipelineManager->forgetAllPipelines();
|
||||
m_pipelinesStale = false;
|
||||
}
|
||||
|
||||
// Free completed texture upload command buffers
|
||||
Assertion(m_textureManager, "Vulkan TextureManager not initialized in setupFrame!");
|
||||
m_textureManager->frameStart();
|
||||
@@ -218,9 +226,10 @@ void VulkanRenderer::flip()
|
||||
});
|
||||
|
||||
// Submit and present
|
||||
auto presentStatus = m_frames[m_currentFrame]->submitAndPresent(m_currentCommandBuffers);
|
||||
auto presentStatus = m_frames[m_currentFrame]->submitAndPresent(m_currentCommandBuffers,
|
||||
m_renderingFinishedSemaphores[m_currentSwapChainImage].get());
|
||||
|
||||
if (presentStatus == SwapChainStatus::eSuboptimal || presentStatus == SwapChainStatus::eOutOfDate) {
|
||||
if (shouldRecreateSwapChain(presentStatus)) {
|
||||
m_swapChainNeedsRecreation = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -326,17 +326,17 @@ vk::Extent2D windowExtent()
|
||||
return {static_cast<uint32_t>(gr_screen.max_w), static_cast<uint32_t>(gr_screen.max_h)};
|
||||
}
|
||||
|
||||
vk::Extent2D chooseSwapChainExtent(const PhysicalDeviceValues& values, uint32_t width, uint32_t height)
|
||||
vk::Extent2D chooseSwapChainExtent(const vk::SurfaceCapabilitiesKHR& capabilities, uint32_t width, uint32_t height)
|
||||
{
|
||||
if (values.surfaceCapabilities.currentExtent.width != UINT32_MAX) {
|
||||
return values.surfaceCapabilities.currentExtent;
|
||||
if (capabilities.currentExtent.width != UINT32_MAX) {
|
||||
return capabilities.currentExtent;
|
||||
} else {
|
||||
VkExtent2D actualExtent = {width, height};
|
||||
|
||||
actualExtent.width = std::max(values.surfaceCapabilities.minImageExtent.width,
|
||||
std::min(values.surfaceCapabilities.maxImageExtent.width, actualExtent.width));
|
||||
actualExtent.height = std::max(values.surfaceCapabilities.minImageExtent.height,
|
||||
std::min(values.surfaceCapabilities.maxImageExtent.height, actualExtent.height));
|
||||
actualExtent.width = std::max(capabilities.minImageExtent.width,
|
||||
std::min(capabilities.maxImageExtent.width, actualExtent.width));
|
||||
actualExtent.height = std::max(capabilities.minImageExtent.height,
|
||||
std::min(capabilities.maxImageExtent.height, actualExtent.height));
|
||||
|
||||
return actualExtent;
|
||||
}
|
||||
@@ -459,8 +459,11 @@ bool VulkanRenderer::initialize()
|
||||
setTextureManager(m_textureManager.get());
|
||||
|
||||
// Initialize shader manager
|
||||
if (m_deviceFeatures.shaderClipDistance != VK_TRUE) {
|
||||
mprintf(("Vulkan: HW clip distance not supported, using software clip distance fallback\n"));
|
||||
}
|
||||
m_shaderManager = std::make_unique<VulkanShaderManager>();
|
||||
if (!m_shaderManager->init(m_device.get())) {
|
||||
if (!m_shaderManager->init(m_device.get(), m_deviceFeatures.shaderClipDistance == VK_TRUE)) {
|
||||
nprintf(("vulkan", "Failed to initialize Vulkan shader manager!\n"));
|
||||
return false;
|
||||
}
|
||||
@@ -1012,6 +1015,7 @@ bool VulkanRenderer::createLogicalDevice(const PhysicalDeviceValues& deviceValue
|
||||
enabledFeatures.fillModeNonSolid = supportedFeatures.fillModeNonSolid; // wireframe (GR_FILL_MODE_WIRE -> vk::PolygonMode::eLine)
|
||||
enabledFeatures.shaderClipDistance = supportedFeatures.shaderClipDistance; // gl_ClipDistance[] in main / shadow-map / default-material vertex shaders
|
||||
enabledFeatures.textureCompressionBC = supportedFeatures.textureCompressionBC; // BC/DXT texture formats (optional; usage gated by isTextureCompressionBCSupported)
|
||||
enabledFeatures.textureCompressionETC2 = supportedFeatures.textureCompressionETC2; // ETC2/EAC texture formats (optional)
|
||||
enabledFeatures.depthClamp = supportedFeatures.depthClamp; // clamp depth in the shadow pass (optional; when absent, CAPABILITY_SHADOWS reports false and shadows are disabled)
|
||||
|
||||
vk::PhysicalDeviceAccelerationStructureFeaturesKHR accelStructFeatures;
|
||||
@@ -1109,7 +1113,7 @@ bool VulkanRenderer::createSwapChain(const PhysicalDeviceValues& deviceValues, v
|
||||
createInfo.minImageCount = imageCount;
|
||||
createInfo.imageFormat = surfaceFormat.format;
|
||||
createInfo.imageColorSpace = surfaceFormat.colorSpace;
|
||||
createInfo.imageExtent = chooseSwapChainExtent(deviceValues, window.width, window.height);
|
||||
createInfo.imageExtent = chooseSwapChainExtent(deviceValues.surfaceCapabilities, window.width, window.height);
|
||||
createInfo.imageArrayLayers = 1;
|
||||
createInfo.imageUsage = vk::ImageUsageFlagBits::eColorAttachment
|
||||
| vk::ImageUsageFlagBits::eTransferSrc
|
||||
@@ -1124,7 +1128,19 @@ bool VulkanRenderer::createSwapChain(const PhysicalDeviceValues& deviceValues, v
|
||||
createInfo.imageSharingMode = vk::SharingMode::eExclusive;
|
||||
}
|
||||
|
||||
createInfo.preTransform = deviceValues.surfaceCapabilities.currentTransform;
|
||||
// preTransform describes the transform already applied to the rendered content.
|
||||
// FSO does not pre-rotate it, so request identity whenever supported. If this
|
||||
// differs from currentTransform (e.g. landscape on an Android phone), the
|
||||
// presentation engine compensates during presentation. Passing currentTransform
|
||||
// instead would claim that FSO had already performed that rotation.
|
||||
auto supported = deviceValues.surfaceCapabilities.supportedTransforms;
|
||||
if (supported & vk::SurfaceTransformFlagBitsKHR::eIdentity) {
|
||||
createInfo.preTransform = vk::SurfaceTransformFlagBitsKHR::eIdentity;
|
||||
}
|
||||
else {
|
||||
createInfo.preTransform = deviceValues.surfaceCapabilities.currentTransform;
|
||||
}
|
||||
|
||||
createInfo.compositeAlpha = vk::CompositeAlphaFlagBitsKHR::eOpaque;
|
||||
createInfo.presentMode = choosePresentMode(deviceValues);
|
||||
createInfo.clipped = true;
|
||||
@@ -1145,6 +1161,7 @@ bool VulkanRenderer::createSwapChain(const PhysicalDeviceValues& deviceValues, v
|
||||
m_hdrActive = (surfaceFormat.colorSpace == vk::ColorSpaceKHR::eHdr10St2084EXT);
|
||||
Gr_hdr_output_active = m_hdrActive;
|
||||
m_swapChainExtent = createInfo.imageExtent;
|
||||
m_swapChainPreTransform = createInfo.preTransform;
|
||||
|
||||
// Without -window_res the render resolution simply is the window, so follow
|
||||
// gr_screen (which gr_screen_resize() has already updated for this
|
||||
@@ -1206,6 +1223,42 @@ bool VulkanRenderer::createSwapChain(const PhysicalDeviceValues& deviceValues, v
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanRenderer::shouldRecreateSwapChain(SwapChainStatus status) const
|
||||
{
|
||||
// An already requested rebuild also avoids redundant Android surface queries.
|
||||
if (m_swapChainNeedsRecreation || status == SwapChainStatus::eOutOfDate) {
|
||||
return true;
|
||||
}
|
||||
if (status != SwapChainStatus::eSuboptimal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
if (m_swapChainPreTransform == vk::SurfaceTransformFlagBitsKHR::eIdentity) {
|
||||
vk::SurfaceCapabilitiesKHR capabilities;
|
||||
try {
|
||||
capabilities = m_physicalDevice.getSurfaceCapabilitiesKHR(m_vkSurface.get());
|
||||
} catch (const vk::SystemError&) {
|
||||
// Let the normal recreation path handle a failed surface query.
|
||||
return true;
|
||||
}
|
||||
if (capabilities.currentTransform != m_swapChainPreTransform &&
|
||||
(capabilities.supportedTransforms & m_swapChainPreTransform)) {
|
||||
const auto window = windowExtent();
|
||||
const auto extent = chooseSwapChainExtent(capabilities, window.width, window.height);
|
||||
if (extent == m_swapChainExtent) {
|
||||
// Android can report SUBOPTIMAL on every present while the compositor
|
||||
// compensates for our identity preTransform. Rebuilding with the same
|
||||
// extent and preTransform would just repeat that condition next frame.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VulkanRenderer::recreateSwapChain()
|
||||
{
|
||||
nprintf(("vulkan", "Vulkan: Recreating swap chain...\n"));
|
||||
@@ -1229,7 +1282,7 @@ bool VulkanRenderer::recreateSwapChain()
|
||||
|
||||
// Check for 0x0 extent (minimized window) — caller should retry later
|
||||
const vk::Extent2D window = windowExtent();
|
||||
auto extent = chooseSwapChainExtent(freshValues, window.width, window.height);
|
||||
auto extent = chooseSwapChainExtent(freshValues.surfaceCapabilities, window.width, window.height);
|
||||
if (extent.width == 0 || extent.height == 0) {
|
||||
nprintf(("vulkan", "Vulkan: Surface extent is 0x0 (minimized), deferring swap chain recreation\n"));
|
||||
return false;
|
||||
@@ -1290,6 +1343,7 @@ bool VulkanRenderer::recreateSwapChain()
|
||||
// Reset swap chain image tracking
|
||||
m_swapChainImageRenderImage.clear();
|
||||
m_swapChainImageRenderImage.resize(m_swapChainImages.size(), nullptr);
|
||||
createRenderingFinishedSemaphores();
|
||||
m_previousSwapChainImage = UINT32_MAX;
|
||||
|
||||
m_swapChainNeedsRecreation = false;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "VulkanShader.h"
|
||||
#include "VulkanShaderCompiler.h"
|
||||
#include "VulkanRenderer.h"
|
||||
#include "gr_vulkan.h"
|
||||
|
||||
namespace graphics::vulkan {
|
||||
|
||||
@@ -29,12 +31,13 @@ void vulkan_recompile_all_shaders(const std::function<void(size_t, size_t)>& pro
|
||||
{
|
||||
auto* shaderManager = getShaderManager();
|
||||
shaderManager->recompileAllShaders(progressCallback);
|
||||
getRendererInstance()->requestPipelineRebuild();
|
||||
}
|
||||
|
||||
VulkanShaderManager::VulkanShaderManager() = default;
|
||||
VulkanShaderManager::~VulkanShaderManager() = default;
|
||||
|
||||
bool VulkanShaderManager::init(vk::Device device)
|
||||
bool VulkanShaderManager::init(vk::Device device, bool hwClipDistance)
|
||||
{
|
||||
if (m_initialized) {
|
||||
return true;
|
||||
@@ -44,7 +47,7 @@ bool VulkanShaderManager::init(vk::Device device)
|
||||
|
||||
// Initialize runtime shader compiler
|
||||
m_compiler = std::make_unique<VulkanShaderCompiler>();
|
||||
if (!m_compiler->init()) {
|
||||
if (!m_compiler->init(hwClipDistance)) {
|
||||
nprintf(("vulkan", "VulkanShaderManager: Failed to initialize shader compiler!\n"));
|
||||
return false;
|
||||
}
|
||||
@@ -127,10 +130,7 @@ void VulkanShaderManager::recompileAllShaders(const std::function<void(size_t, s
|
||||
nprintf(("vulkan", "VulkanShaderManager: Failed to create vertex module: %s\n", e.what()));
|
||||
}
|
||||
}
|
||||
shader.vertexInputMask = 0;
|
||||
for (auto attr : typeInfo->attributes) {
|
||||
shader.vertexInputMask |= (1u << attr);
|
||||
}
|
||||
shader.vertexInputMask = shader_vertex_input_mask(*typeInfo);
|
||||
|
||||
SCP_string fragFile = typeInfo->frag;
|
||||
auto fragSpirv = m_compiler->compile(fragFile, vk::ShaderStageFlagBits::eFragment, type, flags, requiresRaytracing);
|
||||
@@ -146,13 +146,19 @@ void VulkanShaderManager::recompileAllShaders(const std::function<void(size_t, s
|
||||
}
|
||||
|
||||
shader.valid = shader.vertexModule && shader.fragmentModule;
|
||||
// the inactive anti-aliasing family cannot compile under the current mode, so build it again on next use
|
||||
if (!shader.valid) {
|
||||
m_shaderMap.erase(shader_key_t(static_cast<int>(type), flags));
|
||||
m_freeSlots.push_back(static_cast<size_t>(&shader - m_shaders.data()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++current;
|
||||
// callers expect 0..total-1, like OpenGL; the retail options popup waits for current + 1 == total
|
||||
if (progressCallback) {
|
||||
progressCallback(current, total);
|
||||
}
|
||||
++current;
|
||||
}
|
||||
|
||||
nprintf(("vulkan", "VulkanShaderManager: Recompiled %zu shaders\n", total));
|
||||
@@ -216,10 +222,7 @@ int VulkanShaderManager::loadShader(shader_type type, unsigned int flags)
|
||||
vertFile.c_str(), e.what()));
|
||||
}
|
||||
}
|
||||
shader.vertexInputMask = 0;
|
||||
for (auto attr : typeInfo->attributes) {
|
||||
shader.vertexInputMask |= (1u << attr);
|
||||
}
|
||||
shader.vertexInputMask = shader_vertex_input_mask(*typeInfo);
|
||||
|
||||
// Compile fragment shader
|
||||
SCP_string fragFile = typeInfo->frag;
|
||||
|
||||
@@ -59,7 +59,7 @@ public:
|
||||
* @param device Vulkan logical device
|
||||
* @return true on success
|
||||
*/
|
||||
bool init(vk::Device device);
|
||||
bool init(vk::Device device, bool hwClipDistance);
|
||||
|
||||
/**
|
||||
* @brief Shutdown and release all shader modules
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "graphics/post_processing.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <md5.h>
|
||||
#include <shaderc/shaderc.h>
|
||||
|
||||
@@ -102,8 +103,9 @@ VulkanShadercLibrary::VulkanShadercLibrary()
|
||||
VulkanShaderCompiler::VulkanShaderCompiler() = default;
|
||||
VulkanShaderCompiler::~VulkanShaderCompiler() = default;
|
||||
|
||||
bool VulkanShaderCompiler::init()
|
||||
bool VulkanShaderCompiler::init(bool hwClipDistance)
|
||||
{
|
||||
m_hwClipDistance = hwClipDistance;
|
||||
if (m_initialized) {
|
||||
return true;
|
||||
}
|
||||
@@ -148,6 +150,13 @@ SCP_string VulkanShaderCompiler::buildHeader(vk::ShaderStageFlagBits /*stage*/,
|
||||
// shaderc automatically predefines VULKAN=100 when targeting Vulkan,
|
||||
// so we do NOT define it here — doing so causes a "Macro redefined" error.
|
||||
|
||||
// gl_ClipDistance[] requires the shaderClipDistance device feature. When the
|
||||
// device supports it, use the hardware clip path, otherwise
|
||||
// the shaders fall back to the vClipDist varying (software clipping).
|
||||
if (!m_hwClipDistance) {
|
||||
header += "#define SDR_FLAG_SW_CLIP_FALLBACK\n";
|
||||
}
|
||||
|
||||
// Blinn-Phong lighting model (matches OpenGL's opengl_shader_get_header)
|
||||
if (Detail.lighting < 3) {
|
||||
header += "#define FLAG_LIGHT_MODEL_BLINN_PHONG\n";
|
||||
@@ -206,6 +215,28 @@ SCP_string VulkanShaderCompiler::computeSourceHash(const SCP_string& header, con
|
||||
return md5.hexdigest();
|
||||
}
|
||||
|
||||
// rejects a cache file cut short by a crash or full disk: a complete module ends exactly on an OpFunctionEnd
|
||||
static bool isCompleteSpirv(const SCP_vector<uint32_t>& spirv)
|
||||
{
|
||||
constexpr uint32_t SPIRV_MAGIC = 0x07230203;
|
||||
constexpr size_t HEADER_WORDS = 5;
|
||||
constexpr uint32_t OP_FUNCTION_END = 56;
|
||||
if (spirv.size() < HEADER_WORDS || spirv[0] != SPIRV_MAGIC) {
|
||||
return false;
|
||||
}
|
||||
size_t i = HEADER_WORDS;
|
||||
uint32_t lastOpcode = 0;
|
||||
while (i < spirv.size()) {
|
||||
const uint32_t wordCount = spirv[i] >> 16;
|
||||
if (wordCount == 0) {
|
||||
return false;
|
||||
}
|
||||
lastOpcode = spirv[i] & 0xFFFF;
|
||||
i += wordCount;
|
||||
}
|
||||
return i == spirv.size() && lastOpcode == OP_FUNCTION_END;
|
||||
}
|
||||
|
||||
SCP_vector<uint32_t> VulkanShaderCompiler::compile(const SCP_string& filename,
|
||||
vk::ShaderStageFlagBits stage,
|
||||
shader_type sdrType,
|
||||
@@ -241,10 +272,13 @@ SCP_vector<uint32_t> VulkanShaderCompiler::compile(const SCP_string& filename,
|
||||
if (fileSize > 0 && (fileSize % 4) == 0) {
|
||||
SCP_vector<uint32_t> spirv(fileSize / 4);
|
||||
if (cfread(spirv.data(), 1, fileSize, cacheFile) == fileSize) {
|
||||
cfclose(cacheFile);
|
||||
nprintf(("vulkan", "VulkanShaderCompiler: Cache hit for %s (flags=0x%x)\n",
|
||||
filename.c_str(), flags));
|
||||
return spirv;
|
||||
if (isCompleteSpirv(spirv)) {
|
||||
cfclose(cacheFile);
|
||||
nprintf(("vulkan", "VulkanShaderCompiler: Cache hit for %s (flags=0x%x)\n",
|
||||
filename.c_str(), flags));
|
||||
return spirv;
|
||||
}
|
||||
mprintf(("VulkanShaderCompiler: Ignoring incomplete cache file %s\n", cacheFilename.c_str()));
|
||||
}
|
||||
}
|
||||
cfclose(cacheFile);
|
||||
@@ -316,11 +350,29 @@ SCP_vector<uint32_t> VulkanShaderCompiler::compile(const SCP_string& filename,
|
||||
nprintf(("vulkan", "VulkanShaderCompiler: Compiled %s -> %zu bytes SPIR-V\n",
|
||||
filename.c_str(), byteLen));
|
||||
|
||||
// Save to disk cache
|
||||
cacheFile = cfopen(cacheFilename.c_str(), "wb", CF_TYPE_CACHE);
|
||||
if (cacheFile != nullptr) {
|
||||
cfwrite(spirv.data(), static_cast<int>(spirv.size() * sizeof(uint32_t)), 1, cacheFile);
|
||||
cfclose(cacheFile);
|
||||
// each writer renames its own temp file, so a game crash or a second game never leaves a truncated .spv
|
||||
const SCP_string tmpFilename = "vk_shader-" + hash + "-" +
|
||||
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()) +
|
||||
".tmp";
|
||||
const bool complete = isCompleteSpirv(spirv);
|
||||
cacheFile = complete ? cfopen(tmpFilename.c_str(), "wb", CF_TYPE_CACHE) : nullptr;
|
||||
if (!complete) {
|
||||
mprintf(("VulkanShaderCompiler: Not caching %s: compiled module fails the cache check\n",
|
||||
cacheFilename.c_str()));
|
||||
} else if (cacheFile != nullptr) {
|
||||
const bool written =
|
||||
cfwrite(spirv.data(), static_cast<int>(spirv.size() * sizeof(uint32_t)), 1, cacheFile) == 1;
|
||||
const bool closed = cfclose(cacheFile) == 0;
|
||||
bool saved = false;
|
||||
if (written && closed) {
|
||||
// rename() on Windows fails if the target exists
|
||||
cf_delete(cacheFilename.c_str(), CF_TYPE_CACHE);
|
||||
saved = cf_rename(tmpFilename.c_str(), cacheFilename.c_str(), CF_TYPE_CACHE) == CF_RENAME_SUCCESS;
|
||||
}
|
||||
if (!saved) {
|
||||
cf_delete(tmpFilename.c_str(), CF_TYPE_CACHE);
|
||||
mprintf(("VulkanShaderCompiler: Could not write cache file %s\n", cacheFilename.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -337,26 +389,29 @@ void VulkanShaderCompiler::purgeOldCache()
|
||||
const SCP_string PREFIX = "vk_shader-";
|
||||
const auto TIMEOUT = 2.0 * 30.0 * 24.0 * 60.0 * 60.0; // ~2 months in seconds
|
||||
|
||||
SCP_vector<SCP_string> cache_files;
|
||||
SCP_vector<file_list_info> file_info;
|
||||
cf_get_file_list(cache_files, CF_TYPE_CACHE, "*.spv", CF_SORT_NONE, &file_info,
|
||||
CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
|
||||
|
||||
Assertion(cache_files.size() == file_info.size(),
|
||||
"cf_get_file_list returned different sizes for file names and file informations!");
|
||||
|
||||
auto now = std::time(nullptr);
|
||||
for (size_t i = 0; i < cache_files.size(); ++i) {
|
||||
auto& name = cache_files[i];
|
||||
for (const char* ext : {".spv", ".tmp"}) {
|
||||
SCP_vector<SCP_string> cache_files;
|
||||
SCP_vector<file_list_info> file_info;
|
||||
const SCP_string filter = SCP_string("*") + ext;
|
||||
cf_get_file_list(cache_files, CF_TYPE_CACHE, filter.c_str(), CF_SORT_NONE, &file_info,
|
||||
CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
|
||||
|
||||
if (name.compare(0, PREFIX.size(), PREFIX) != 0) {
|
||||
continue; // Not our cache file
|
||||
}
|
||||
Assertion(cache_files.size() == file_info.size(),
|
||||
"cf_get_file_list returned different sizes for file names and file informations!");
|
||||
|
||||
auto diff = std::difftime(now, file_info[i].write_time);
|
||||
if (diff > TIMEOUT) {
|
||||
auto full_name = name + ".spv";
|
||||
cf_delete(full_name.c_str(), CF_TYPE_CACHE);
|
||||
for (size_t i = 0; i < cache_files.size(); ++i) {
|
||||
auto& name = cache_files[i];
|
||||
|
||||
if (name.compare(0, PREFIX.size(), PREFIX) != 0) {
|
||||
continue; // Not our cache file
|
||||
}
|
||||
|
||||
auto diff = std::difftime(now, file_info[i].write_time);
|
||||
if (diff > TIMEOUT) {
|
||||
auto full_name = name + ext;
|
||||
cf_delete(full_name.c_str(), CF_TYPE_CACHE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
VulkanShaderCompiler();
|
||||
~VulkanShaderCompiler();
|
||||
|
||||
bool init();
|
||||
bool init(bool hwClipDistance);
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
@@ -69,6 +69,7 @@ private:
|
||||
|
||||
std::unique_ptr<VulkanShadercLibrary> m_shaderc;
|
||||
bool m_initialized = false;
|
||||
bool m_hwClipDistance = false;
|
||||
};
|
||||
|
||||
} // namespace graphics::vulkan
|
||||
@@ -351,9 +351,9 @@ void vulkan_zbias(int bias)
|
||||
if (bias) {
|
||||
drawManager->setDepthBiasEnabled(true);
|
||||
if (bias < 0) {
|
||||
stateTracker->setDepthBias(1.0f, static_cast<float>(-bias));
|
||||
stateTracker->setDepthBias(static_cast<float>(-bias), 1.0f);
|
||||
} else {
|
||||
stateTracker->setDepthBias(0.0f, static_cast<float>(-bias));
|
||||
stateTracker->setDepthBias(static_cast<float>(-bias), 0.0f);
|
||||
}
|
||||
} else {
|
||||
drawManager->setDepthBiasEnabled(false);
|
||||
|
||||
@@ -293,7 +293,7 @@ class VulkanStateTracker {
|
||||
*
|
||||
* Must be called before every draw command to ensure dirty dynamic state
|
||||
* (viewport, scissor, depth bias, stencil ref, line width) is flushed.
|
||||
* applyMaterial() sets depth bias/stencil AFTER bindPipeline(), so if
|
||||
* applyMaterial() sets the stencil reference AFTER bindPipeline(), so if
|
||||
* the pipeline didn't change, those changes would be lost without this.
|
||||
*/
|
||||
void applyDynamicState();
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
#include "gr_vulkan.h"
|
||||
|
||||
#include "graphics/util/pixel_swizzle.h"
|
||||
#include "bmpman/bm_internal.h"
|
||||
#include "bmpman/bmpman.h"
|
||||
#include "ddsutils/ddsutils.h"
|
||||
#include "ddsutils/bcdec.h"
|
||||
#include "globalincs/systemvars.h"
|
||||
#include "ktxutils/ktxutils.h"
|
||||
#include "ktxutils/etcdec.h"
|
||||
|
||||
namespace graphics::vulkan {
|
||||
|
||||
@@ -820,15 +822,15 @@ bool VulkanTextureManager::uploadCubemap(int handle, bitmap* bm, int compType)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t mipLevels = 1;
|
||||
size_t blockSize = 0;
|
||||
|
||||
if (isCompressed) {
|
||||
blockSize = get_compressed_block_size(baseCompType);
|
||||
mipLevels = static_cast<uint32_t>(bm_get_num_mipmaps(handle));
|
||||
mipLevels = std::max<uint32_t>(mipLevels, 1);
|
||||
}
|
||||
|
||||
// uncompressed DDS cubemaps also store every face's mip chain (face0[mips], face1[mips], ...)
|
||||
auto mipLevels = static_cast<uint32_t>(bm_get_num_mipmaps(handle));
|
||||
mipLevels = std::max<uint32_t>(mipLevels, 1);
|
||||
|
||||
// A cubemap is six array layers; each face has the same layout.
|
||||
TextureUploadLayout layout;
|
||||
layout.width = faceW;
|
||||
@@ -841,6 +843,19 @@ bool VulkanTextureManager::uploadCubemap(int handle, bitmap* bm, int compType)
|
||||
size_t perFaceSize = layerByteSize(layout);
|
||||
size_t totalDataSize = perFaceSize * 6;
|
||||
|
||||
// OpenGL builds the chain for one-level cubemaps, and the env map is sampled at a roughness-based mip
|
||||
uint32_t imageMipLevels = mipLevels;
|
||||
bool generateMips = false;
|
||||
if (!isCompressed && mipLevels == 1) {
|
||||
const auto fmtProps = m_physicalDevice.getFormatProperties(format);
|
||||
if ((fmtProps.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterLinear) &&
|
||||
(fmtProps.optimalTilingFeatures & vk::FormatFeatureFlagBits::eBlitSrc) &&
|
||||
(fmtProps.optimalTilingFeatures & vk::FormatFeatureFlagBits::eBlitDst)) {
|
||||
imageMipLevels = calculateMipLevels(faceW, faceH);
|
||||
generateMips = imageMipLevels > 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Defer destruction of existing resources
|
||||
if (ts->image) {
|
||||
auto* deletionQueue = getDeletionQueue();
|
||||
@@ -852,10 +867,14 @@ bool VulkanTextureManager::uploadCubemap(int handle, bitmap* bm, int compType)
|
||||
ts->image = nullptr;
|
||||
ts->allocation = VulkanAllocation{};
|
||||
}
|
||||
ts->isCubemap = false;
|
||||
|
||||
// Create cubemap image (6 layers, eCubeCompatible)
|
||||
vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled;
|
||||
if (!createImage(faceW, faceH, mipLevels, format, vk::ImageTiling::eOptimal,
|
||||
if (generateMips) {
|
||||
usage |= vk::ImageUsageFlagBits::eTransferSrc;
|
||||
}
|
||||
if (!createImage(faceW, faceH, imageMipLevels, format, vk::ImageTiling::eOptimal,
|
||||
usage, MemoryUsage::GpuOnly, ts->image, ts->allocation, 6, true)) {
|
||||
nprintf(("vulkan", "VulkanTexture: uploadCubemap: failed to create cubemap image\n"));
|
||||
return false;
|
||||
@@ -863,7 +882,7 @@ bool VulkanTextureManager::uploadCubemap(int handle, bitmap* bm, int compType)
|
||||
|
||||
// Create cubemap image view (samplerCube)
|
||||
ts->imageView = createImageView(ts->image, format, vk::ImageAspectFlagBits::eColor,
|
||||
mipLevels, ImageViewType::Cube, 6);
|
||||
imageMipLevels, ImageViewType::Cube, 6);
|
||||
if (!ts->imageView) {
|
||||
nprintf(("vulkan", "VulkanTexture: uploadCubemap: failed to create cube image view\n"));
|
||||
m_device.destroyImage(ts->image);
|
||||
@@ -888,15 +907,20 @@ bool VulkanTextureManager::uploadCubemap(int handle, bitmap* bm, int compType)
|
||||
if (!mapped) {
|
||||
m_memoryManager->freeAllocation(stagingAllocation);
|
||||
m_device.destroyBuffer(stagingBuffer);
|
||||
m_device.destroyImageView(ts->imageView);
|
||||
ts->imageView = nullptr;
|
||||
m_device.destroyImage(ts->image);
|
||||
ts->image = nullptr;
|
||||
m_memoryManager->freeAllocation(ts->allocation);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy data to staging buffer
|
||||
// DDS cubemap data layout: face0[mip0..mipN], face1[mip0..mipN], ..., face5[mip0..mipN]
|
||||
if (!isCompressed && bm->bpp == 24) {
|
||||
// Convert BGR to BGRA for all 6 faces
|
||||
// Convert BGR to BGRA for all 6 faces and their mips
|
||||
graphics::util::expand_BGR_to_BGRA(reinterpret_cast<const uint8_t*>(bm->data),
|
||||
static_cast<uint8_t*>(mapped), static_cast<size_t>(faceW) * faceH * 6);
|
||||
static_cast<uint8_t*>(mapped), (perFaceSize / 4) * 6);
|
||||
} else {
|
||||
memcpy(mapped, reinterpret_cast<const void*>(bm->data), totalDataSize);
|
||||
}
|
||||
@@ -913,15 +937,18 @@ bool VulkanTextureManager::uploadCubemap(int handle, bitmap* bm, int compType)
|
||||
|
||||
// Record transitions + copy and submit async
|
||||
vk::CommandBuffer cmd = beginSingleTimeCommands();
|
||||
if (generateMips) {
|
||||
copyRegions.clear();
|
||||
}
|
||||
recordUploadCommands(cmd, ts->image, stagingBuffer, format, faceW, faceH,
|
||||
mipLevels, vk::ImageLayout::eUndefined, false, copyRegions, 6);
|
||||
imageMipLevels, vk::ImageLayout::eUndefined, generateMips, copyRegions, 6);
|
||||
submitUploadAsync(cmd, stagingBuffer, stagingAllocation);
|
||||
|
||||
// Update slot info
|
||||
ts->width = faceW;
|
||||
ts->height = faceH;
|
||||
ts->format = format;
|
||||
ts->mipLevels = mipLevels;
|
||||
ts->mipLevels = imageMipLevels;
|
||||
ts->bpp = bm->bpp;
|
||||
ts->arrayLayers = 6;
|
||||
ts->bitmapHandle = handle;
|
||||
@@ -932,7 +959,7 @@ bool VulkanTextureManager::uploadCubemap(int handle, bitmap* bm, int compType)
|
||||
ts->vScale = 1.0f;
|
||||
|
||||
nprintf(("vulkan", "VulkanTexture: Cubemap uploaded: %ux%u, %u mips, format=%d\n",
|
||||
faceW, faceH, mipLevels, static_cast<int>(format)));
|
||||
faceW, faceH, imageMipLevels, static_cast<int>(format)));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1106,6 +1133,10 @@ bool VulkanTextureManager::bm_data(int handle, bitmap* bm, int compType)
|
||||
isCubemapUpload = (compType == DDS_CUBEMAP_DXT1 || compType == DDS_CUBEMAP_DXT3 ||
|
||||
compType == DDS_CUBEMAP_DXT5);
|
||||
}
|
||||
// bm_is_compressed() returns 0 for uncompressed DDS cubemaps; only comp_type identifies them
|
||||
if (!isCubemapUpload) {
|
||||
isCubemapUpload = bm_get_entry(handle)->comp_type == BM_TYPE_CUBEMAP_DDS;
|
||||
}
|
||||
|
||||
if (isCubemapUpload) {
|
||||
return uploadCubemap(handle, bm, compType);
|
||||
@@ -1857,7 +1888,13 @@ ubyte* VulkanTextureManager::get_bitmap_from_texture(int bitmap_num, int* width_
|
||||
ts->format == vk::Format::eBc1RgbaUnormBlock ||
|
||||
ts->format == vk::Format::eBc2UnormBlock ||
|
||||
ts->format == vk::Format::eBc3UnormBlock ||
|
||||
ts->format == vk::Format::eBc7UnormBlock,
|
||||
ts->format == vk::Format::eBc7UnormBlock ||
|
||||
ts->format == vk::Format::eEtc2R8G8B8UnormBlock ||
|
||||
ts->format == vk::Format::eEtc2R8G8B8SrgbBlock ||
|
||||
ts->format == vk::Format::eEtc2R8G8B8A1UnormBlock ||
|
||||
ts->format == vk::Format::eEtc2R8G8B8A1SrgbBlock ||
|
||||
ts->format == vk::Format::eEtc2R8G8B8A8UnormBlock ||
|
||||
ts->format == vk::Format::eEtc2R8G8B8A8SrgbBlock,
|
||||
"get_bitmap_from_texture: unsupported format %d on bitmap %d"
|
||||
" — may be a render target or dynamic texture",
|
||||
static_cast<int>(ts->format), bitmap_num);
|
||||
@@ -1868,7 +1905,13 @@ ubyte* VulkanTextureManager::get_bitmap_from_texture(int bitmap_num, int* width_
|
||||
const bool isCompressed = (ts->format == vk::Format::eBc1RgbaUnormBlock ||
|
||||
ts->format == vk::Format::eBc2UnormBlock ||
|
||||
ts->format == vk::Format::eBc3UnormBlock ||
|
||||
ts->format == vk::Format::eBc7UnormBlock);
|
||||
ts->format == vk::Format::eBc7UnormBlock ||
|
||||
ts->format == vk::Format::eEtc2R8G8B8UnormBlock ||
|
||||
ts->format == vk::Format::eEtc2R8G8B8SrgbBlock ||
|
||||
ts->format == vk::Format::eEtc2R8G8B8A1UnormBlock ||
|
||||
ts->format == vk::Format::eEtc2R8G8B8A1SrgbBlock ||
|
||||
ts->format == vk::Format::eEtc2R8G8B8A8UnormBlock ||
|
||||
ts->format == vk::Format::eEtc2R8G8B8A8SrgbBlock);
|
||||
const bool hasAlpha = bm_has_alpha_channel(bitmap_num);
|
||||
const int outChannels = hasAlpha ? 4 : 3;
|
||||
|
||||
@@ -1885,6 +1928,12 @@ ubyte* VulkanTextureManager::get_bitmap_from_texture(int bitmap_num, int* width_
|
||||
case vk::Format::eBc2UnormBlock: blockSize = BCDEC_BC2_BLOCK_SIZE; break;
|
||||
case vk::Format::eBc3UnormBlock: blockSize = BCDEC_BC3_BLOCK_SIZE; break;
|
||||
case vk::Format::eBc7UnormBlock: blockSize = BCDEC_BC7_BLOCK_SIZE; break;
|
||||
case vk::Format::eEtc2R8G8B8UnormBlock:
|
||||
case vk::Format::eEtc2R8G8B8SrgbBlock: blockSize = ETCDEC_ETC_RGB_BLOCK_SIZE; break;
|
||||
case vk::Format::eEtc2R8G8B8A1UnormBlock:
|
||||
case vk::Format::eEtc2R8G8B8A1SrgbBlock: blockSize = ETCDEC_ETC_RGB_A1_BLOCK_SIZE; break;
|
||||
case vk::Format::eEtc2R8G8B8A8UnormBlock:
|
||||
case vk::Format::eEtc2R8G8B8A8SrgbBlock: blockSize = ETCDEC_EAC_RGBA_BLOCK_SIZE; break;
|
||||
default: return nullptr;
|
||||
}
|
||||
blockW = (w + 3) / 4;
|
||||
@@ -2038,6 +2087,18 @@ void VulkanTextureManager::decodeReadbackBuffer(const void* mapped, vk::Format f
|
||||
case vk::Format::eBc7UnormBlock:
|
||||
bcdec_bc7(srcBlock, blockColBase, static_cast<int>(copyW * 4));
|
||||
break;
|
||||
case vk::Format::eEtc2R8G8B8UnormBlock:
|
||||
case vk::Format::eEtc2R8G8B8SrgbBlock:
|
||||
etcdec_etc_rgb(srcBlock, blockColBase, static_cast<int>(copyW * 4));
|
||||
break;
|
||||
case vk::Format::eEtc2R8G8B8A1UnormBlock:
|
||||
case vk::Format::eEtc2R8G8B8A1SrgbBlock:
|
||||
etcdec_etc_rgb_a1(srcBlock, blockColBase, static_cast<int>(copyW * 4));
|
||||
break;
|
||||
case vk::Format::eEtc2R8G8B8A8UnormBlock:
|
||||
case vk::Format::eEtc2R8G8B8A8SrgbBlock:
|
||||
etcdec_eac_rgba(srcBlock, blockColBase, static_cast<int>(copyW * 4));
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
srcBlock += blockSize;
|
||||
@@ -2174,6 +2235,46 @@ tcache_slot_vulkan* VulkanTextureManager::getTextureSlot(int handle)
|
||||
return static_cast<tcache_slot_vulkan*>(slot->gr_info);
|
||||
}
|
||||
|
||||
bool VulkanTextureManager::ensureCubemapUploaded(int handle)
|
||||
{
|
||||
if (handle < 0) {
|
||||
return false;
|
||||
}
|
||||
auto* ts = getTextureSlot(handle);
|
||||
if (ts != nullptr && ts->imageView && ts->isCubemap) {
|
||||
return true;
|
||||
}
|
||||
// one warning and no per-frame retry for a map that cannot be used; handles are reused, signatures are not
|
||||
const uint signature = bm_get_entry(handle)->signature;
|
||||
if (signature == m_failedCubemapSignature) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool uploaded = false;
|
||||
// the cubemap kind lives in comp_type; type is just BM_TYPE_DDS
|
||||
const BM_TYPE type = bm_get_entry(handle)->comp_type;
|
||||
if (type == BM_TYPE_CUBEMAP_DDS || type == BM_TYPE_CUBEMAP_DXT1 || type == BM_TYPE_CUBEMAP_DXT3 ||
|
||||
type == BM_TYPE_CUBEMAP_DXT5) {
|
||||
// same lock as OpenGL's opengl_determine_bpp_and_flags for TCACHE_TYPE_CUBEMAP; DXT data stays compressed
|
||||
const int compType = bm_is_compressed(handle);
|
||||
const int bpp = bm_has_alpha_channel(handle) ? 32 : 24;
|
||||
// bm_lock() uploads through bm_data(); upload here only if that did not leave a cube
|
||||
bitmap* bmp = bm_lock(handle, static_cast<ubyte>(bpp), BMP_TEX_OTHER);
|
||||
if (bmp != nullptr) {
|
||||
ts = getTextureSlot(handle);
|
||||
uploaded = (ts != nullptr && ts->imageView && ts->isCubemap) || uploadCubemap(handle, bmp, compType);
|
||||
bm_unlock(handle);
|
||||
}
|
||||
}
|
||||
|
||||
if (!uploaded) {
|
||||
m_failedCubemapSignature = signature;
|
||||
Warning(LOCATION, "Environment map '%s' could not be uploaded as a cubemap; reflections are off.",
|
||||
bm_get_filename(handle));
|
||||
}
|
||||
return uploaded;
|
||||
}
|
||||
|
||||
vk::Format VulkanTextureManager::bppToVkFormat(int bpp, bool compressed, int compressionType)
|
||||
{
|
||||
if (compressed) {
|
||||
@@ -2719,7 +2820,7 @@ void VulkanTextureManager::recordUploadCommands(vk::CommandBuffer cmd, vk::Image
|
||||
region.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
|
||||
region.imageSubresource.mipLevel = 0;
|
||||
region.imageSubresource.baseArrayLayer = 0;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
region.imageSubresource.layerCount = arrayLayers;
|
||||
region.imageOffset = vk::Offset3D(0, 0, 0);
|
||||
region.imageExtent = vk::Extent3D(width, height, 1);
|
||||
|
||||
|
||||
@@ -194,6 +194,13 @@ public:
|
||||
*/
|
||||
tcache_slot_vulkan* getTextureSlot(int handle);
|
||||
|
||||
/**
|
||||
* @brief Upload a cubemap bitmap as a cubemap if it has not been uploaded yet
|
||||
* @param[in] handle bitmap handle of a DDS cubemap (compressed or not) or a cubemap render target
|
||||
* @return true if the handle now has a cubemap image view
|
||||
*/
|
||||
bool ensureCubemapUploaded(int handle);
|
||||
|
||||
/**
|
||||
* @brief Bitmap handle of the render target currently bound via bm_set_render_target,
|
||||
* or -1 when rendering to the default framebuffer. Used by gr.screenToBlob so a
|
||||
@@ -355,9 +362,9 @@ private:
|
||||
/**
|
||||
* @brief Decode a get_bitmap_from_texture() readback buffer into data_out
|
||||
*
|
||||
* Handles both the BC-compressed (block-decode via bcdec) and uncompressed
|
||||
* (per-format channel expand/convert) cases; data_out is tightly packed
|
||||
* w*h pixels at outChannels (3 or 4) bytes each.
|
||||
* Handles BC- and ETC2-compressed textures (block-decode via bcdec/etcdec)
|
||||
* and uncompressed textures (per-format channel expand/convert); data_out
|
||||
* is tightly packed w*h pixels at outChannels (3 or 4) bytes each.
|
||||
*/
|
||||
static void decodeReadbackBuffer(const void* mapped, vk::Format format, bool isCompressed,
|
||||
uint32_t w, uint32_t h, uint32_t blockW, uint32_t blockH,
|
||||
@@ -382,6 +389,7 @@ private:
|
||||
|
||||
vk::Device m_device;
|
||||
vk::PhysicalDevice m_physicalDevice;
|
||||
uint m_failedCubemapSignature = 0;
|
||||
VulkanMemoryManager* m_memoryManager = nullptr;
|
||||
vk::CommandPool m_commandPool;
|
||||
vk::Queue m_graphicsQueue;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "ddsutils/ddsutils.h"
|
||||
#include "ddsutils/bcdec.h"
|
||||
#include "globalincs/systemvars.h"
|
||||
|
||||
#include "ktxutils/ktxutils.h"
|
||||
|
||||
namespace graphics::vulkan {
|
||||
|
||||
@@ -28,11 +28,11 @@ int vulkan_preload(int bitmap_num, int /*is_aabitmap*/)
|
||||
}
|
||||
|
||||
// Determine lock parameters based on compression type.
|
||||
// For compressed DDS textures, lock with the matching DXT/BC7 flags to get
|
||||
// raw compressed data with all pre-baked mipmap levels.
|
||||
// For compressed DDS/KTX textures, lock with the matching DXT/BC7/ETC2
|
||||
// flags to get raw compressed data with all pre-baked mipmap levels.
|
||||
int compType = bm_is_compressed(bitmap_num);
|
||||
int lockBpp = 32;
|
||||
ubyte lockFlags = BMP_TEX_XPARENT;
|
||||
uint lockFlags = BMP_TEX_XPARENT;
|
||||
|
||||
switch (compType) {
|
||||
case DDS_DXT1:
|
||||
@@ -60,6 +60,21 @@ int vulkan_preload(int bitmap_num, int /*is_aabitmap*/)
|
||||
lockBpp = 32;
|
||||
lockFlags = BMP_TEX_CUBEMAP;
|
||||
break;
|
||||
case KTX_ETC2_RGB:
|
||||
case KTX_ETC2_SRGB:
|
||||
lockBpp = 24;
|
||||
lockFlags = BMP_TEX_ETC2_RGB8;
|
||||
break;
|
||||
case KTX_ETC2_RGBA_EAC:
|
||||
case KTX_ETC2_SRGBA_EAC:
|
||||
lockBpp = 32;
|
||||
lockFlags = BMP_TEX_ETC2_RGBA8;
|
||||
break;
|
||||
case KTX_ETC2_RGB_A1:
|
||||
case KTX_ETC2_SRGB_A1:
|
||||
lockBpp = 24;
|
||||
lockFlags = BMP_TEX_ETC2_RGBA1;
|
||||
break;
|
||||
default:
|
||||
// Uncompressed — use 32bpp decompressed
|
||||
compType = 0;
|
||||
|
||||
Loaded 100 of 168 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user