summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/config/project_settings.cpp1
-rw-r--r--core/core_constants.cpp1
-rw-r--r--core/io/image.cpp43
-rw-r--r--core/io/image.h12
-rw-r--r--core/math/triangle_mesh.cpp16
-rw-r--r--core/math/triangle_mesh.h7
-rw-r--r--core/object/object.h1
-rw-r--r--core/os/os.cpp4
-rw-r--r--core/os/os.h1
-rw-r--r--doc/classes/@GlobalScope.xml14
-rw-r--r--doc/classes/AudioEffectAmplify.xml2
-rw-r--r--doc/classes/AudioEffectBandLimitFilter.xml1
-rw-r--r--doc/classes/AudioEffectBandPassFilter.xml1
-rw-r--r--doc/classes/AudioEffectCapture.xml1
-rw-r--r--doc/classes/AudioEffectChorus.xml1
-rw-r--r--doc/classes/AudioEffectCompressor.xml1
-rw-r--r--doc/classes/AudioEffectDelay.xml1
-rw-r--r--doc/classes/AudioEffectEQ.xml1
-rw-r--r--doc/classes/AudioEffectEQ10.xml1
-rw-r--r--doc/classes/AudioEffectEQ21.xml1
-rw-r--r--doc/classes/AudioEffectEQ6.xml3
-rw-r--r--doc/classes/AudioEffectHighPassFilter.xml3
-rw-r--r--doc/classes/AudioEffectHighShelfFilter.xml3
-rw-r--r--doc/classes/AudioEffectLimiter.xml1
-rw-r--r--doc/classes/AudioEffectLowPassFilter.xml3
-rw-r--r--doc/classes/AudioEffectLowShelfFilter.xml3
-rw-r--r--doc/classes/AudioEffectNotchFilter.xml1
-rw-r--r--doc/classes/AudioEffectPanner.xml3
-rw-r--r--doc/classes/AudioEffectPhaser.xml3
-rw-r--r--doc/classes/AudioEffectPitchShift.xml3
-rw-r--r--doc/classes/AudioEffectReverb.xml4
-rw-r--r--doc/classes/AudioEffectStereoEnhance.xml1
-rw-r--r--doc/classes/Image.xml37
-rw-r--r--doc/classes/MovieWriter.xml76
-rw-r--r--doc/classes/ORMMaterial3D.xml3
-rw-r--r--doc/classes/ProjectSettings.xml27
-rw-r--r--doc/classes/StandardMaterial3D.xml4
-rw-r--r--doc/classes/TileData.xml3
-rwxr-xr-xdoc/tools/make_rst.py6
-rw-r--r--drivers/gles3/SCsub1
-rw-r--r--drivers/gles3/environment/SCsub5
-rw-r--r--drivers/gles3/environment/gi.cpp140
-rw-r--r--drivers/gles3/environment/gi.h99
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp3
-rw-r--r--drivers/gles3/rasterizer_gles3.h3
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp100
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.h41
-rw-r--r--drivers/vulkan/rendering_device_vulkan.cpp6
-rw-r--r--editor/editor_node.cpp44
-rw-r--r--editor/editor_node.h4
-rw-r--r--editor/editor_properties.cpp6
-rw-r--r--editor/editor_run.cpp12
-rw-r--r--editor/editor_run.h2
-rw-r--r--editor/icons/MainMovieWrite.svg1
-rw-r--r--editor/icons/MainMovieWriteEnabled.svg1
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp2
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp6
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp6
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp17
-rw-r--r--editor/project_settings_editor.cpp4
-rw-r--r--editor/scene_tree_editor.cpp3
-rw-r--r--main/main.cpp55
-rw-r--r--modules/jpg/SCsub1
-rw-r--r--modules/jpg/image_loader_jpegd.cpp56
-rw-r--r--modules/minimp3/audio_stream_mp3.cpp4
-rw-r--r--modules/vorbis/audio_stream_ogg_vorbis.cpp17
-rw-r--r--modules/webp/image_loader_webp.cpp181
-rw-r--r--modules/webp/image_loader_webp.h4
-rw-r--r--modules/webp/register_types.cpp10
-rw-r--r--modules/webp/resource_saver_webp.cpp90
-rw-r--r--modules/webp/resource_saver_webp.h49
-rw-r--r--modules/webp/webp_common.cpp190
-rw-r--r--modules/webp/webp_common.h45
-rw-r--r--scene/2d/tile_map.cpp4
-rw-r--r--scene/animation/animation_node_state_machine.cpp22
-rw-r--r--scene/animation/animation_node_state_machine.h6
-rw-r--r--scene/animation/animation_player.cpp7
-rw-r--r--scene/animation/animation_tree.cpp55
-rw-r--r--scene/gui/control.cpp4
-rw-r--r--scene/resources/mesh.cpp11
-rw-r--r--scene/resources/resource_format_text.cpp38
-rw-r--r--scene/resources/tile_set.cpp6
-rw-r--r--scene/resources/tile_set.h8
-rw-r--r--servers/SCsub1
-rw-r--r--servers/audio/audio_driver_dummy.cpp67
-rw-r--r--servers/audio/audio_driver_dummy.h22
-rw-r--r--servers/audio/audio_stream.cpp29
-rw-r--r--servers/movie_writer/SCsub5
-rw-r--r--servers/movie_writer/movie_writer.cpp306
-rw-r--r--servers/movie_writer/movie_writer.h123
-rw-r--r--servers/movie_writer/movie_writer_mjpeg.cpp263
-rw-r--r--servers/movie_writer/movie_writer_mjpeg.h73
-rw-r--r--servers/register_server_types.cpp15
-rw-r--r--servers/rendering/dummy/environment/gi.h85
-rw-r--r--servers/rendering/dummy/rasterizer_dummy.h3
-rw-r--r--servers/rendering/dummy/rasterizer_storage_dummy.h41
-rw-r--r--servers/rendering/environment/renderer_gi.h85
-rw-r--r--servers/rendering/renderer_compositor.h2
-rw-r--r--servers/rendering/renderer_rd/SCsub1
-rw-r--r--servers/rendering/renderer_rd/effects/resolve.cpp130
-rw-r--r--servers/rendering/renderer_rd/effects/resolve.h74
-rw-r--r--servers/rendering/renderer_rd/effects_rd.cpp56
-rw-r--r--servers/rendering/renderer_rd/effects_rd.h24
-rw-r--r--servers/rendering/renderer_rd/environment/SCsub5
-rw-r--r--servers/rendering/renderer_rd/environment/gi.cpp (renamed from servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp)1336
-rw-r--r--servers/rendering/renderer_rd/environment/gi.h (renamed from servers/rendering/renderer_rd/renderer_scene_gi_rd.h)201
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp204
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h15
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp14
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h4
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp3
-rw-r--r--servers/rendering/renderer_rd/renderer_compositor_rd.h14
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp69
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.h21
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.cpp344
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.h86
-rw-r--r--servers/rendering/renderer_rd/shaders/SCsub1
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/resolve.glsl (renamed from servers/rendering/renderer_rd/shaders/resolve.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/SCsub17
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/gi.glsl (renamed from servers/rendering/renderer_rd/shaders/gi.glsl)44
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl (renamed from servers/rendering/renderer_rd/shaders/sdfgi_debug.glsl)10
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl (renamed from servers/rendering/renderer_rd/shaders/sdfgi_debug_probes.glsl)48
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl (renamed from servers/rendering/renderer_rd/shaders/sdfgi_direct_light.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl (renamed from servers/rendering/renderer_rd/shaders/sdfgi_integrate.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/sdfgi_preprocess.glsl (renamed from servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/voxel_gi.glsl (renamed from servers/rendering/renderer_rd/shaders/voxel_gi.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/voxel_gi_debug.glsl (renamed from servers/rendering/renderer_rd/shaders/voxel_gi_debug.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/voxel_gi_sdf.glsl (renamed from servers/rendering/renderer_rd/shaders/voxel_gi_sdf.glsl)0
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl20
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl8
-rw-r--r--servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl7
-rw-r--r--servers/rendering/renderer_scene_cull.cpp2
-rw-r--r--servers/rendering/renderer_storage.h42
-rw-r--r--servers/rendering/renderer_viewport.cpp14
-rw-r--r--servers/rendering/renderer_viewport.h2
-rw-r--r--servers/rendering/rendering_server_default.cpp8
-rw-r--r--servers/rendering/rendering_server_default.h6
-rw-r--r--servers/rendering/rendering_server_globals.cpp1
-rw-r--r--servers/rendering/rendering_server_globals.h2
-rw-r--r--servers/rendering_server.h2
-rw-r--r--thirdparty/README.md3
-rw-r--r--thirdparty/jpeg-compressor/jpge.cpp1076
-rw-r--r--thirdparty/jpeg-compressor/jpge.h174
-rw-r--r--thirdparty/openxr/include/openxr/openxr.h595
-rw-r--r--thirdparty/openxr/include/openxr/openxr_platform.h19
-rw-r--r--thirdparty/openxr/include/openxr/openxr_reflection.h357
-rw-r--r--thirdparty/openxr/src/common/xr_linear.h6
-rw-r--r--thirdparty/openxr/src/xr_generated_dispatch_table.c30
-rw-r--r--thirdparty/openxr/src/xr_generated_dispatch_table.h30
149 files changed, 6271 insertions, 1641 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index c65a2a5bc6..7145e628c1 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -41,7 +41,6 @@
#include "core/os/keyboard.h"
#include "core/variant/variant_parser.h"
#include "core/version.h"
-
#include "modules/modules_enabled.gen.h" // For mono.
const String ProjectSettings::PROJECT_DATA_DIR_NAME_SUFFIX = "godot";
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 4e0845e625..1f46223a1d 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -607,6 +607,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_OBJECT_TOO_BIG);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_VALID_TYPES);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_SAVE_FILE);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_SAVE_FILE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_OBJECTID);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_POINTER);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ARRAY_TYPE);
diff --git a/core/io/image.cpp b/core/io/image.cpp
index dfba45c4e9..a945d3e6cd 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -81,9 +81,14 @@ const char *Image::format_names[Image::FORMAT_MAX] = {
};
SavePNGFunc Image::save_png_func = nullptr;
+SaveJPGFunc Image::save_jpg_func = nullptr;
SaveEXRFunc Image::save_exr_func = nullptr;
SavePNGBufferFunc Image::save_png_buffer_func = nullptr;
+SaveJPGBufferFunc Image::save_jpg_buffer_func = nullptr;
+
+SaveWebPFunc Image::save_webp_func = nullptr;
+SaveWebPBufferFunc Image::save_webp_buffer_func = nullptr;
void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixel_size, uint8_t *p_data, const uint8_t *p_pixel) {
uint32_t ofs = (p_y * width + p_x) * p_pixel_size;
@@ -2286,6 +2291,14 @@ Error Image::save_png(const String &p_path) const {
return save_png_func(p_path, Ref<Image>((Image *)this));
}
+Error Image::save_jpg(const String &p_path, float p_quality) const {
+ if (save_jpg_func == nullptr) {
+ return ERR_UNAVAILABLE;
+ }
+
+ return save_jpg_func(p_path, Ref<Image>((Image *)this), p_quality);
+}
+
Vector<uint8_t> Image::save_png_to_buffer() const {
if (save_png_buffer_func == nullptr) {
return Vector<uint8_t>();
@@ -2294,6 +2307,14 @@ Vector<uint8_t> Image::save_png_to_buffer() const {
return save_png_buffer_func(Ref<Image>((Image *)this));
}
+Vector<uint8_t> Image::save_jpg_to_buffer(float p_quality) const {
+ if (save_jpg_buffer_func == nullptr) {
+ return Vector<uint8_t>();
+ }
+
+ return save_jpg_buffer_func(Ref<Image>((Image *)this), p_quality);
+}
+
Error Image::save_exr(const String &p_path, bool p_grayscale) const {
if (save_exr_func == nullptr) {
return ERR_UNAVAILABLE;
@@ -2302,6 +2323,24 @@ Error Image::save_exr(const String &p_path, bool p_grayscale) const {
return save_exr_func(p_path, Ref<Image>((Image *)this), p_grayscale);
}
+Error Image::save_webp(const String &p_path, const bool p_lossy, const float p_quality) const {
+ if (save_webp_func == nullptr) {
+ return ERR_UNAVAILABLE;
+ }
+ ERR_FAIL_COND_V_MSG(p_lossy && !(0.0f <= p_quality && p_quality <= 1.0f), ERR_INVALID_PARAMETER, "The WebP lossy quality was set to " + rtos(p_quality) + ", which is not valid. WebP lossy quality must be between 0.0 and 1.0 (inclusive).");
+
+ return save_webp_func(p_path, Ref<Image>((Image *)this), p_lossy, p_quality);
+}
+
+Vector<uint8_t> Image::save_webp_to_buffer(const bool p_lossy, const float p_quality) const {
+ if (save_webp_buffer_func == nullptr) {
+ return Vector<uint8_t>();
+ }
+ ERR_FAIL_COND_V_MSG(p_lossy && !(0.0f <= p_quality && p_quality <= 1.0f), Vector<uint8_t>(), "The WebP lossy quality was set to " + rtos(p_quality) + ", which is not valid. WebP lossy quality must be between 0.0 and 1.0 (inclusive).");
+
+ return save_webp_buffer_func(Ref<Image>((Image *)this), p_lossy, p_quality);
+}
+
int Image::get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps) {
int mm;
return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmaps ? -1 : 0);
@@ -3138,7 +3177,11 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("load", "path"), &Image::load);
ClassDB::bind_method(D_METHOD("save_png", "path"), &Image::save_png);
ClassDB::bind_method(D_METHOD("save_png_to_buffer"), &Image::save_png_to_buffer);
+ ClassDB::bind_method(D_METHOD("save_jpg", "path", "quality"), &Image::save_jpg, DEFVAL(0.75));
+ ClassDB::bind_method(D_METHOD("save_jpg_to_buffer", "quality"), &Image::save_jpg_to_buffer, DEFVAL(0.75));
ClassDB::bind_method(D_METHOD("save_exr", "path", "grayscale"), &Image::save_exr, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("save_webp", "path", "lossy", "quality"), &Image::save_webp, DEFVAL(false), DEFVAL(0.75f));
+ ClassDB::bind_method(D_METHOD("save_webp_to_buffer", "lossy", "quality"), &Image::save_webp_to_buffer, DEFVAL(false), DEFVAL(0.75f));
ClassDB::bind_method(D_METHOD("detect_alpha"), &Image::detect_alpha);
ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible);
diff --git a/core/io/image.h b/core/io/image.h
index 1025554d51..229103f792 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -45,7 +45,11 @@ class Image;
typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img);
typedef Vector<uint8_t> (*SavePNGBufferFunc)(const Ref<Image> &p_img);
+typedef Error (*SaveJPGFunc)(const String &p_path, const Ref<Image> &p_img, float p_quality);
+typedef Vector<uint8_t> (*SaveJPGBufferFunc)(const Ref<Image> &p_img, float p_quality);
typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
+typedef Error (*SaveWebPFunc)(const String &p_path, const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
+typedef Vector<uint8_t> (*SaveWebPBufferFunc)(const Ref<Image> &p_img, const bool p_lossy, const float p_quality);
typedef Error (*SaveEXRFunc)(const String &p_path, const Ref<Image> &p_img, bool p_grayscale);
@@ -54,8 +58,12 @@ class Image : public Resource {
public:
static SavePNGFunc save_png_func;
+ static SaveJPGFunc save_jpg_func;
static SaveEXRFunc save_exr_func;
static SavePNGBufferFunc save_png_buffer_func;
+ static SaveJPGBufferFunc save_jpg_buffer_func;
+ static SaveWebPFunc save_webp_func;
+ static SaveWebPBufferFunc save_webp_buffer_func;
enum {
MAX_WIDTH = (1 << 24), // force a limit somehow
@@ -281,8 +289,12 @@ public:
Error load(const String &p_path);
Error save_png(const String &p_path) const;
+ Error save_jpg(const String &p_path, float p_quality = 0.75) const;
Vector<uint8_t> save_png_to_buffer() const;
+ Vector<uint8_t> save_jpg_to_buffer(float p_quality = 0.75) const;
Error save_exr(const String &p_path, bool p_grayscale) const;
+ Error save_webp(const String &p_path, const bool p_lossy = false, const float p_quality = 0.75f) const;
+ Vector<uint8_t> save_webp_to_buffer(const bool p_lossy = false, const float p_quality = 0.75f) const;
void create_empty(int p_width, int p_height, bool p_use_mipmaps, Format p_format) {
create(p_width, p_height, p_use_mipmaps, p_format);
diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp
index 54461bf70f..4433559e6d 100644
--- a/core/math/triangle_mesh.cpp
+++ b/core/math/triangle_mesh.cpp
@@ -104,9 +104,11 @@ void TriangleMesh::get_indices(Vector<int> *r_triangles_indices) const {
}
}
-void TriangleMesh::create(const Vector<Vector3> &p_faces) {
+void TriangleMesh::create(const Vector<Vector3> &p_faces, const Vector<int32_t> &p_surface_indices) {
valid = false;
+ ERR_FAIL_COND(p_surface_indices.size() && p_surface_indices.size() != p_faces.size());
+
int fc = p_faces.size();
ERR_FAIL_COND(!fc || ((fc % 3) != 0));
fc /= 3;
@@ -121,6 +123,7 @@ void TriangleMesh::create(const Vector<Vector3> &p_faces) {
//goes in-place.
const Vector3 *r = p_faces.ptr();
+ const int32_t *si = p_surface_indices.ptr();
Triangle *w = triangles.ptrw();
HashMap<Vector3, int> db;
@@ -148,6 +151,7 @@ void TriangleMesh::create(const Vector<Vector3> &p_faces) {
}
f.normal = Face3(r[i * 3 + 0], r[i * 3 + 1], r[i * 3 + 2]).get_plane().get_normal();
+ f.surface_index = si ? si[i] : 0;
bw[i].left = -1;
bw[i].right = -1;
@@ -264,7 +268,7 @@ Vector3 TriangleMesh::get_area_normal(const AABB &p_aabb) const {
return n;
}
-bool TriangleMesh::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal) const {
+bool TriangleMesh::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int32_t *r_surf_index) const {
uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth);
enum {
@@ -317,6 +321,9 @@ bool TriangleMesh::intersect_segment(const Vector3 &p_begin, const Vector3 &p_en
d = nd;
r_point = res;
r_normal = f3.get_plane().get_normal();
+ if (r_surf_index) {
+ *r_surf_index = s.surface_index;
+ }
inters = true;
}
}
@@ -366,7 +373,7 @@ bool TriangleMesh::intersect_segment(const Vector3 &p_begin, const Vector3 &p_en
return inters;
}
-bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal) const {
+bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal, int32_t *r_surf_index) const {
uint32_t *stack = (uint32_t *)alloca(sizeof(int) * max_depth);
enum {
@@ -417,6 +424,9 @@ bool TriangleMesh::intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, V
d = nd;
r_point = res;
r_normal = f3.get_plane().get_normal();
+ if (r_surf_index) {
+ *r_surf_index = s.surface_index;
+ }
inters = true;
}
}
diff --git a/core/math/triangle_mesh.h b/core/math/triangle_mesh.h
index 1b99945698..166b4adb7a 100644
--- a/core/math/triangle_mesh.h
+++ b/core/math/triangle_mesh.h
@@ -41,6 +41,7 @@ public:
struct Triangle {
Vector3 normal;
int indices[3];
+ int32_t surface_index;
};
private:
@@ -81,8 +82,8 @@ private:
public:
bool is_valid() const;
- bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal) const;
- bool intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal) const;
+ bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int32_t *r_surf_index = nullptr) const;
+ bool intersect_ray(const Vector3 &p_begin, const Vector3 &p_dir, Vector3 &r_point, Vector3 &r_normal, int32_t *r_surf_index = nullptr) const;
bool intersect_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const;
bool inside_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, Vector3 p_scale = Vector3(1, 1, 1)) const;
Vector3 get_area_normal(const AABB &p_aabb) const;
@@ -92,7 +93,7 @@ public:
const Vector<Vector3> &get_vertices() const { return vertices; }
void get_indices(Vector<int> *r_triangles_indices) const;
- void create(const Vector<Vector3> &p_faces);
+ void create(const Vector<Vector3> &p_faces, const Vector<int32_t> &p_surface_indices = Vector<int32_t>());
TriangleMesh();
};
diff --git a/core/object/object.h b/core/object/object.h
index 8117783fae..02dd875acf 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -85,6 +85,7 @@ enum PropertyHint {
PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send
PROPERTY_HINT_NODE_PATH_VALID_TYPES,
PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog
+ PROPERTY_HINT_GLOBAL_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog
PROPERTY_HINT_INT_IS_OBJECTID,
PROPERTY_HINT_ARRAY_TYPE,
PROPERTY_HINT_INT_IS_POINTER,
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 327f1c95f2..93477f4288 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -388,6 +388,10 @@ bool OS::has_feature(const String &p_feature) {
return true;
}
+ if (p_feature == "movie") {
+ return _writing_movie;
+ }
+
#ifdef DEBUG_ENABLED
if (p_feature == "debug") {
return true;
diff --git a/core/os/os.h b/core/os/os.h
index 157b8ab992..c6ea9d869a 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -58,6 +58,7 @@ class OS {
bool _allow_layered = false;
bool _stdout_enabled = true;
bool _stderr_enabled = true;
+ bool _writing_movie = false;
CompositeLogger *_logger = nullptr;
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 9f4041fa2b..4048b483e8 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -2569,19 +2569,21 @@
</constant>
<constant name="PROPERTY_HINT_SAVE_FILE" value="38" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_INT_IS_OBJECTID" value="39" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_GLOBAL_SAVE_FILE" value="39" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_INT_IS_POINTER" value="41" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_INT_IS_OBJECTID" value="40" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_ARRAY_TYPE" value="40" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_INT_IS_POINTER" value="42" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_LOCALE_ID" value="42" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_ARRAY_TYPE" value="41" enum="PropertyHint">
+ </constant>
+ <constant name="PROPERTY_HINT_LOCALE_ID" value="43" enum="PropertyHint">
Hints that a string property is a locale code. Editing it will show a locale dialog for picking language and country.
</constant>
- <constant name="PROPERTY_HINT_LOCALIZABLE_STRING" value="43" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_LOCALIZABLE_STRING" value="44" enum="PropertyHint">
Hints that a dictionary property is string translation map. Dictionary keys are locale codes and, values are translated strings.
</constant>
- <constant name="PROPERTY_HINT_MAX" value="44" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_MAX" value="45" enum="PropertyHint">
</constant>
<constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags">
</constant>
diff --git a/doc/classes/AudioEffectAmplify.xml b/doc/classes/AudioEffectAmplify.xml
index 7ae0b06dad..43fa9d6ad3 100644
--- a/doc/classes/AudioEffectAmplify.xml
+++ b/doc/classes/AudioEffectAmplify.xml
@@ -2,12 +2,12 @@
<class name="AudioEffectAmplify" inherits="AudioEffect" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Adds an amplifying audio effect to an audio bus.
- Increases or decreases the volume of the selected audio bus.
</brief_description>
<description>
Increases or decreases the volume being routed through the audio bus.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
<members>
<member name="volume_db" type="float" setter="set_volume_db" getter="get_volume_db" default="0.0">
diff --git a/doc/classes/AudioEffectBandLimitFilter.xml b/doc/classes/AudioEffectBandLimitFilter.xml
index 3635f122f2..4efa431714 100644
--- a/doc/classes/AudioEffectBandLimitFilter.xml
+++ b/doc/classes/AudioEffectBandLimitFilter.xml
@@ -7,5 +7,6 @@
Limits the frequencies in a range around the [member AudioEffectFilter.cutoff_hz] and allows frequencies outside of this range to pass.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
</class>
diff --git a/doc/classes/AudioEffectBandPassFilter.xml b/doc/classes/AudioEffectBandPassFilter.xml
index 5e7713f3d4..6820b2c9f8 100644
--- a/doc/classes/AudioEffectBandPassFilter.xml
+++ b/doc/classes/AudioEffectBandPassFilter.xml
@@ -7,5 +7,6 @@
Attenuates the frequencies inside of a range around the [member AudioEffectFilter.cutoff_hz] and cuts frequencies outside of this band.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
</class>
diff --git a/doc/classes/AudioEffectCapture.xml b/doc/classes/AudioEffectCapture.xml
index 7a7f1f8e8a..8e02056456 100644
--- a/doc/classes/AudioEffectCapture.xml
+++ b/doc/classes/AudioEffectCapture.xml
@@ -8,6 +8,7 @@
Application code should consume these audio frames from this ring buffer using [method get_buffer] and process it as needed, for example to capture data from a microphone, implement application defined effects, or to transmit audio over the network. When capturing audio data from a microphone, the format of the samples will be stereo 32-bit floating point PCM.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
<methods>
<method name="can_get_buffer" qualifiers="const">
diff --git a/doc/classes/AudioEffectChorus.xml b/doc/classes/AudioEffectChorus.xml
index ca04126da4..5efba17e6a 100644
--- a/doc/classes/AudioEffectChorus.xml
+++ b/doc/classes/AudioEffectChorus.xml
@@ -7,6 +7,7 @@
Adds a chorus audio effect. The effect applies a filter with voices to duplicate the audio source and manipulate it through the filter.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
<methods>
<method name="get_voice_cutoff_hz" qualifiers="const">
diff --git a/doc/classes/AudioEffectCompressor.xml b/doc/classes/AudioEffectCompressor.xml
index 5ad948feba..8793ec0a02 100644
--- a/doc/classes/AudioEffectCompressor.xml
+++ b/doc/classes/AudioEffectCompressor.xml
@@ -13,6 +13,7 @@
- Accentuates transients by using a wider attack, making effects sound more punchy.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
<members>
<member name="attack_us" type="float" setter="set_attack_us" getter="get_attack_us" default="20.0">
diff --git a/doc/classes/AudioEffectDelay.xml b/doc/classes/AudioEffectDelay.xml
index 8a95e315cd..8223ccd6bd 100644
--- a/doc/classes/AudioEffectDelay.xml
+++ b/doc/classes/AudioEffectDelay.xml
@@ -8,6 +8,7 @@
Plays input signal back after a period of time. The delayed signal may be played back multiple times to create the sound of a repeating, decaying echo. Delay effects range from a subtle echo effect to a pronounced blending of previous sounds with new sounds.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
<members>
<member name="dry" type="float" setter="set_dry" getter="get_dry" default="1.0">
diff --git a/doc/classes/AudioEffectEQ.xml b/doc/classes/AudioEffectEQ.xml
index 908ae956ed..ce5b6de3be 100644
--- a/doc/classes/AudioEffectEQ.xml
+++ b/doc/classes/AudioEffectEQ.xml
@@ -8,6 +8,7 @@
AudioEffectEQ gives you control over frequencies. Use it to compensate for existing deficiencies in audio. AudioEffectEQs are useful on the Master bus to completely master a mix and give it more character. They are also useful when a game is run on a mobile device, to adjust the mix to that kind of speakers (it can be added but disabled when headphones are plugged).
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
<methods>
<method name="get_band_count" qualifiers="const">
diff --git a/doc/classes/AudioEffectEQ10.xml b/doc/classes/AudioEffectEQ10.xml
index 0b03ea99dd..8cf53b9df2 100644
--- a/doc/classes/AudioEffectEQ10.xml
+++ b/doc/classes/AudioEffectEQ10.xml
@@ -19,5 +19,6 @@
See also [AudioEffectEQ], [AudioEffectEQ6], [AudioEffectEQ21].
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
</class>
diff --git a/doc/classes/AudioEffectEQ21.xml b/doc/classes/AudioEffectEQ21.xml
index 6861a74596..2eed3b4836 100644
--- a/doc/classes/AudioEffectEQ21.xml
+++ b/doc/classes/AudioEffectEQ21.xml
@@ -30,5 +30,6 @@
See also [AudioEffectEQ], [AudioEffectEQ6], [AudioEffectEQ10].
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
</class>
diff --git a/doc/classes/AudioEffectEQ6.xml b/doc/classes/AudioEffectEQ6.xml
index 5aea8efaa9..816d4b9b38 100644
--- a/doc/classes/AudioEffectEQ6.xml
+++ b/doc/classes/AudioEffectEQ6.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioEffectEQ6" inherits="AudioEffectEQ" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Adds a 6-band equalizer audio effect to an Audio bus. Gives you control over frequencies from 32 Hz to 10000 Hz.
+ Adds a 6-band equalizer audio effect to an audio bus. Gives you control over frequencies from 32 Hz to 10000 Hz.
Each frequency can be modulated between -60/+24 dB.
</brief_description>
<description>
@@ -15,5 +15,6 @@
See also [AudioEffectEQ], [AudioEffectEQ10], [AudioEffectEQ21].
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
</class>
diff --git a/doc/classes/AudioEffectHighPassFilter.xml b/doc/classes/AudioEffectHighPassFilter.xml
index 02d8dbfffc..53e6f3ca63 100644
--- a/doc/classes/AudioEffectHighPassFilter.xml
+++ b/doc/classes/AudioEffectHighPassFilter.xml
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioEffectHighPassFilter" inherits="AudioEffectFilter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Adds a high-pass filter to the Audio Bus.
+ Adds a high-pass filter to the audio bus.
</brief_description>
<description>
Cuts frequencies lower than the [member AudioEffectFilter.cutoff_hz] and allows higher frequencies to pass.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
</class>
diff --git a/doc/classes/AudioEffectHighShelfFilter.xml b/doc/classes/AudioEffectHighShelfFilter.xml
index bb5cf5c4a0..f2c20862fb 100644
--- a/doc/classes/AudioEffectHighShelfFilter.xml
+++ b/doc/classes/AudioEffectHighShelfFilter.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioEffectHighShelfFilter" inherits="AudioEffectFilter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Reduces all frequencies above the [member AudioEffectFilter.cutoff_hz].
+ Adds a high-shelf filter to the audio bus.
</brief_description>
<description>
+ Reduces all frequencies above the [member AudioEffectFilter.cutoff_hz].
</description>
<tutorials>
<link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
diff --git a/doc/classes/AudioEffectLimiter.xml b/doc/classes/AudioEffectLimiter.xml
index 3fd099b55e..e841889b2a 100644
--- a/doc/classes/AudioEffectLimiter.xml
+++ b/doc/classes/AudioEffectLimiter.xml
@@ -8,6 +8,7 @@
Soft clipping starts to reduce the peaks a little below the threshold level and progressively increases its effect as the input level increases such that the threshold is never exceeded.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
<members>
<member name="ceiling_db" type="float" setter="set_ceiling_db" getter="get_ceiling_db" default="-0.1">
diff --git a/doc/classes/AudioEffectLowPassFilter.xml b/doc/classes/AudioEffectLowPassFilter.xml
index 8a9ca04354..b0577f19c3 100644
--- a/doc/classes/AudioEffectLowPassFilter.xml
+++ b/doc/classes/AudioEffectLowPassFilter.xml
@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioEffectLowPassFilter" inherits="AudioEffectFilter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Adds a low-pass filter to the Audio bus.
+ Adds a low-pass filter to the audio bus.
</brief_description>
<description>
Cuts frequencies higher than the [member AudioEffectFilter.cutoff_hz] and allows lower frequencies to pass.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
</class>
diff --git a/doc/classes/AudioEffectLowShelfFilter.xml b/doc/classes/AudioEffectLowShelfFilter.xml
index 04cc1ab1d8..f654ba3612 100644
--- a/doc/classes/AudioEffectLowShelfFilter.xml
+++ b/doc/classes/AudioEffectLowShelfFilter.xml
@@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioEffectLowShelfFilter" inherits="AudioEffectFilter" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Reduces all frequencies below the [member AudioEffectFilter.cutoff_hz].
+ Adds a low-shelf filter to the audio bus.
</brief_description>
<description>
+ Reduces all frequencies below the [member AudioEffectFilter.cutoff_hz].
</description>
<tutorials>
<link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
diff --git a/doc/classes/AudioEffectNotchFilter.xml b/doc/classes/AudioEffectNotchFilter.xml
index a2cc764003..ad4577cfbc 100644
--- a/doc/classes/AudioEffectNotchFilter.xml
+++ b/doc/classes/AudioEffectNotchFilter.xml
@@ -7,5 +7,6 @@
Attenuates frequencies in a narrow band around the [member AudioEffectFilter.cutoff_hz] and cuts frequencies outside of this range.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
</class>
diff --git a/doc/classes/AudioEffectPanner.xml b/doc/classes/AudioEffectPanner.xml
index 1dd2d9fa27..291e86214e 100644
--- a/doc/classes/AudioEffectPanner.xml
+++ b/doc/classes/AudioEffectPanner.xml
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioEffectPanner" inherits="AudioEffect" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Adds a panner audio effect to an Audio bus. Pans sound left or right.
+ Adds a panner audio effect to an audio bus. Pans sound left or right.
</brief_description>
<description>
Determines how much of an audio signal is sent to the left and right buses.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
<members>
<member name="pan" type="float" setter="set_pan" getter="get_pan" default="0.0">
diff --git a/doc/classes/AudioEffectPhaser.xml b/doc/classes/AudioEffectPhaser.xml
index 9c10052b6a..7aaf404352 100644
--- a/doc/classes/AudioEffectPhaser.xml
+++ b/doc/classes/AudioEffectPhaser.xml
@@ -1,13 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioEffectPhaser" inherits="AudioEffect" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Adds a phaser audio effect to an Audio bus.
+ Adds a phaser audio effect to an audio bus.
Combines the original signal with a copy that is slightly out of phase with the original.
</brief_description>
<description>
Combines phase-shifted signals with the original signal. The movement of the phase-shifted signals is controlled using a low-frequency oscillator.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
<members>
<member name="depth" type="float" setter="set_depth" getter="get_depth" default="1.0">
diff --git a/doc/classes/AudioEffectPitchShift.xml b/doc/classes/AudioEffectPitchShift.xml
index aab380aa72..952d432879 100644
--- a/doc/classes/AudioEffectPitchShift.xml
+++ b/doc/classes/AudioEffectPitchShift.xml
@@ -1,13 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="AudioEffectPitchShift" inherits="AudioEffect" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
- Adds a pitch-shifting audio effect to an Audio bus.
+ Adds a pitch-shifting audio effect to an audio bus.
Raises or lowers the pitch of original sound.
</brief_description>
<description>
Allows modulation of pitch independently of tempo. All frequencies can be increased/decreased with minimal effect on transients.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
<members>
<member name="fft_size" type="int" setter="set_fft_size" getter="get_fft_size" enum="AudioEffectPitchShift.FFTSize" default="3">
diff --git a/doc/classes/AudioEffectReverb.xml b/doc/classes/AudioEffectReverb.xml
index 5525102806..b024f06849 100644
--- a/doc/classes/AudioEffectReverb.xml
+++ b/doc/classes/AudioEffectReverb.xml
@@ -2,12 +2,12 @@
<class name="AudioEffectReverb" inherits="AudioEffect" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Adds a reverberation audio effect to an Audio bus.
- Simulates the sound of acoustic environments such as rooms, concert halls, caverns, or an open spaces.
</brief_description>
<description>
- Simulates rooms of different sizes. Its parameters can be adjusted to simulate the sound of a specific room.
+ Simulates the sound of acoustic environments such as rooms, concert halls, caverns, or an open spaces.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
<link title="Third Person Shooter Demo">https://godotengine.org/asset-library/asset/678</link>
</tutorials>
<members>
diff --git a/doc/classes/AudioEffectStereoEnhance.xml b/doc/classes/AudioEffectStereoEnhance.xml
index 5c62ba982f..088d61189f 100644
--- a/doc/classes/AudioEffectStereoEnhance.xml
+++ b/doc/classes/AudioEffectStereoEnhance.xml
@@ -7,6 +7,7 @@
An audio effect that can be used to adjust the intensity of stereo panning.
</description>
<tutorials>
+ <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link>
</tutorials>
<members>
<member name="pan_pullout" type="float" setter="set_pan_pullout" getter="get_pan_pullout" default="1.0">
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index da5b907fab..d2baf78a9e 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -380,16 +380,51 @@
[b]Note:[/b] The TinyEXR module is disabled in non-editor builds, which means [method save_exr] will return [constant ERR_UNAVAILABLE] when it is called from an exported project.
</description>
</method>
+ <method name="save_jpg" qualifiers="const">
+ <return type="int" enum="Error" />
+ <argument index="0" name="path" type="String" />
+ <argument index="1" name="quality" type="float" default="0.75" />
+ <description>
+ Saves the image as a JPEG file to [code]path[/code] with the specified [code]quality[/code] between [code]0.01[/code] and [code]1.0[/code] (inclusive). Higher [code]quality[/code] values result in better-looking output at the cost of larger file sizes. Recommended [code]quality[/code] values are between [code]0.75[/code] and [code]0.90[/code]. Even at quality [code]1.00[/code], JPEG compression remains lossy.
+ [b]Note:[/b] JPEG does not save an alpha channel. If the [Image] contains an alpha channel, the image will still be saved, but the resulting JPEG file won't contain the alpha channel.
+ </description>
+ </method>
+ <method name="save_jpg_to_buffer" qualifiers="const">
+ <return type="PackedByteArray" />
+ <argument index="0" name="quality" type="float" default="0.75" />
+ <description>
+ Saves the image as a JPEG file to a byte array with the specified [code]quality[/code] between [code]0.01[/code] and [code]1.0[/code] (inclusive). Higher [code]quality[/code] values result in better-looking output at the cost of larger byte array sizes (and therefore memory usage). Recommended [code]quality[/code] values are between [code]0.75[/code] and [code]0.90[/code]. Even at quality [code]1.00[/code], JPEG compression remains lossy.
+ [b]Note:[/b] JPEG does not save an alpha channel. If the [Image] contains an alpha channel, the image will still be saved, but the resulting byte array won't contain the alpha channel.
+ </description>
+ </method>
<method name="save_png" qualifiers="const">
<return type="int" enum="Error" />
<argument index="0" name="path" type="String" />
<description>
- Saves the image as a PNG file to [code]path[/code].
+ Saves the image as a PNG file to the file at [code]path[/code].
</description>
</method>
<method name="save_png_to_buffer" qualifiers="const">
<return type="PackedByteArray" />
<description>
+ Saves the image as a PNG file to a byte array.
+ </description>
+ </method>
+ <method name="save_webp" qualifiers="const">
+ <return type="int" enum="Error" />
+ <argument index="0" name="path" type="String" />
+ <argument index="1" name="lossy" type="bool" default="false" />
+ <argument index="2" name="quality" type="float" default="0.75" />
+ <description>
+ Saves the image as a WebP (Web Picture) file to the file at [code]path[/code]. By default it will save lossless. If [code]lossy[/code] is true, the image will be saved lossy, using the [code]quality[/code] setting between 0.0 and 1.0 (inclusive).
+ </description>
+ </method>
+ <method name="save_webp_to_buffer" qualifiers="const">
+ <return type="PackedByteArray" />
+ <argument index="0" name="lossy" type="bool" default="false" />
+ <argument index="1" name="quality" type="float" default="0.75" />
+ <description>
+ Saves the image as a WebP (Web Picture) file to a byte array. By default it will save lossless. If [code]lossy[/code] is true, the image will be saved lossy, using the [code]quality[/code] setting between 0.0 and 1.0 (inclusive).
</description>
</method>
<method name="set_pixel">
diff --git a/doc/classes/MovieWriter.xml b/doc/classes/MovieWriter.xml
new file mode 100644
index 0000000000..9c713bd7ae
--- /dev/null
+++ b/doc/classes/MovieWriter.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="MovieWriter" inherits="Object" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
+ <brief_description>
+ Abstract class for non-real-time video recording encoders.
+ </brief_description>
+ <description>
+ Godot can record videos with non-real-time simulation. Like the [code]--fixed-fps[/code] command line argument, this forces the reported [code]delta[/code] in [method Node._process] functions to be identical across frames, regardless of how long it actually took to render the frame. This can be used to record high-quality videos with perfect frame pacing regardless of your hardware's capabilities.
+ Godot has 2 built-in [MovieWriter]s:
+ - AVI container with MJPEG for video and uncompressed audio ([code].avi[/code] file extension). Lossy compression, medium file sizes, fast encoding. The lossy compression quality can be adjusted by changing [member ProjectSettings.editor/movie_writer/mjpeg_quality]. The resulting file can be viewed in most video players, but it must be converted to another format for viewing on the web or by Godot with [VideoStreamPlayer]. AVI output is currently limited to a file of 4 GB in size at most.
+ - PNG image sequence for video and WAV for audio ([code].png[/code] file extension). Lossless compression, large file sizes, slow encoding. Designed to be encoded to a video file with another tool such as [url=https://ffmpeg.org/]FFmpeg[/url] after recording. Transparency is currently not supported.
+ If you need to encode to a different format or pipe a stream through third-party software, you can extend the [MovieWriter] class to create your own movie writers. This should typically be done using GDExtension for performance reasons.
+ [b]Editor usage:[/b] A default movie file path can be specified in [member ProjectSettings.editor/movie_writer/movie_file]. Alternatively, for running single scenes, a [code]movie_path[/code] metadata can be added to the root node, specifying the path to a movie file that will be used when recording that scene. Once a path is set, click the video reel icon in the top-right corner of the editor to enable Movie Maker mode, then run any scene as usual. The engine will start recording as soon as the splash screen is finished, and it will only stop recording when the engine quits. Click the video reel icon again to disable Movie Maker mode. Note that toggling Movie Maker mode does not affect project instances that are already running.
+ [b]Note:[/b] MovieWriter is available for use in both the editor and exported projects, but it is [i]not[/i] designed for use by end users to record videos while playing. Players wishing to record gameplay videos should install tools such as [url=https://obsproject.com/]OBS Studio[/url] or [url=https://www.maartenbaert.be/simplescreenrecorder/]SimpleScreenRecorder[/url] instead.
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="_get_audio_mix_rate" qualifiers="virtual const">
+ <return type="int" />
+ <description>
+ Called when the audio sample rate used for recording the audio is requested by the engine. The value returned must be specified in Hz. Defaults to 48000 Hz if [method _get_audio_mix_rate] is not overridden.
+ </description>
+ </method>
+ <method name="_get_audio_speaker_mode" qualifiers="virtual const">
+ <return type="int" enum="AudioServer.SpeakerMode" />
+ <description>
+ Called when the audio speaker mode used for recording the audio is requested by the engine. This can affect the number of output channels in the resulting audio file/stream. Defaults to [constant AudioServer.SPEAKER_MODE_STEREO] if [method _get_audio_speaker_mode] is not overridden.
+ </description>
+ </method>
+ <method name="_handles_file" qualifiers="virtual const">
+ <return type="bool" />
+ <argument index="0" name="path" type="String" />
+ <description>
+ Called when the engine determines whether this [MovieWriter] is able to handle the file at [code]path[/code]. Must return [code]true[/code] if this [MovieWriter] is able to handle the given file path, [code]false[/code] otherwise. Typically, [method _handles_file] is overridden as follows to allow the user to record a file at any path with a given file extension:
+ [codeblock]
+ func _handles_file(path):
+ # Allows specifying an output file with a `.mkv` file extension (case-insensitive),
+ # either in the Project Settings or with the `--write-movie &lt;path&gt;` command line argument.
+ return path.get_extension().to_lower() == "mkv"
+ [/codeblock]
+ </description>
+ </method>
+ <method name="_write_begin" qualifiers="virtual">
+ <return type="int" enum="Error" />
+ <argument index="0" name="movie_size" type="Vector2i" />
+ <argument index="1" name="fps" type="int" />
+ <argument index="2" name="base_path" type="String" />
+ <description>
+ Called once before the engine starts writing video and audio data. [code]movie_size[/code] is the width and height of the video to save. [code]fps[/code] is the number of frames per second specified in the project settings or using the [code]--fixed-fps &lt;fps&gt;[/code] command line argument.
+ </description>
+ </method>
+ <method name="_write_end" qualifiers="virtual">
+ <return type="void" />
+ <description>
+ Called when the engine finishes writing. This occurs when the engine quits by pressing the window manager's close button, or when [method SceneTree.quit] is called.
+ [b]Note:[/b] Pressing [kbd]Ctrl + C[/kbd] on the terminal running the editor/project does [i]not[/i] result in [method _write_end] being called.
+ </description>
+ </method>
+ <method name="_write_frame" qualifiers="virtual">
+ <return type="int" enum="Error" />
+ <argument index="0" name="frame_image" type="Image" />
+ <argument index="1" name="audio_frame_block" type="const void*" />
+ <description>
+ Called at the end of every rendered frame. The [code]frame_image[/code] and [code]audio_frame_block[/code] function arguments should be written to.
+ </description>
+ </method>
+ <method name="add_writer" qualifiers="static">
+ <return type="void" />
+ <argument index="0" name="writer" type="MovieWriter" />
+ <description>
+ Adds a writer to be usable by the engine. The supported file extensions can be set by overridding [method _handles_file].
+ [b]Note:[/b] [method add_writer] must be called early enough in the engine initialization to work, as movie writing is designed to start at the same time as the rest of the engine.
+ </description>
+ </method>
+ </methods>
+</class>
diff --git a/doc/classes/ORMMaterial3D.xml b/doc/classes/ORMMaterial3D.xml
index 69d238ceff..656594a3c3 100644
--- a/doc/classes/ORMMaterial3D.xml
+++ b/doc/classes/ORMMaterial3D.xml
@@ -1,9 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ORMMaterial3D" inherits="BaseMaterial3D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Physically based rendering (PBR) material that can be applied to 3D objects, can use an ORM texture.
</brief_description>
<description>
+ ORMMaterial3D's properties are inherited from [BaseMaterial3D]. Unlike [StandardMaterial3D], ORMMaterial3D uses a single texture for ambient occlusion, roughness and metallic maps, known as an ORM texture.
</description>
<tutorials>
+ <link title="Standard Material 3D and ORM Material 3D">$DOCS_URL/tutorials/3d/standard_material_3d.html</link>
</tutorials>
</class>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 6fd3f9f9fc..fc86b67c60 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -553,6 +553,33 @@
See [enum DisplayServer.VSyncMode] for possible values and how they affect the behavior of your application.
Depending on the platform and used renderer, the engine will fall back to [code]Enabled[/code], if the desired mode is not supported.
</member>
+ <member name="editor/movie_writer/disable_vsync" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], requests V-Sync to be disabled when writing a movie (similar to setting [member display/window/vsync/vsync_mode] to [b]Disabled[/b]). This can speed up video writing if the hardware is fast enough to render, encode and save the video at a framerate higher than the monitor's refresh rate.
+ [b]Note:[/b] [member editor/movie_writer/disable_vsync] has no effect if the operating system or graphics driver forces V-Sync with no way for applications to disable it.
+ </member>
+ <member name="editor/movie_writer/fps" type="int" setter="" getter="" default="60">
+ The number of frames per second to record in the video when writing a movie. Simulation speed will adjust to always match the specified framerate, which means the engine will appear to run slower at higher [member editor/movie_writer/fps] values. Certain FPS values will require you to adjust [member editor/movie_writer/mix_rate_hz] to prevent audio from desynchronizing over time.
+ This can be specified manually on the command line using the [code]--fixed-fps &lt;fps&gt;[/code] command line argument.
+ </member>
+ <member name="editor/movie_writer/mix_rate_hz" type="int" setter="" getter="" default="48000">
+ The audio mix rate to use in the recorded audio when writing a movie (in Hz). This can be different from [member audio/driver/mix_rate], but this value must be divisible by [member editor/movie_writer/fps] to prevent audio from desynchronizing over time.
+ </member>
+ <member name="editor/movie_writer/mjpeg_quality" type="float" setter="" getter="" default="0.75">
+ The JPEG quality to use when writing a video to an AVI file, between [code]0.01[/code] and [code]1.0[/code] (inclusive). Higher [code]quality[/code] values result in better-looking output at the cost of larger file sizes. Recommended [code]quality[/code] values are between [code]0.75[/code] and [code]0.90[/code]. Even at quality [code]1.00[/code], JPEG compression remains lossy.
+ [b]Note:[/b] JPEG does not saving an alpha channel. If the [Image] contains an alpha channel, the image will still be saved, but the resulting JPEG file won't contain the alpha channel.
+ [b]Note:[/b] This does not affect the audio quality or writing PNG image sequences.
+ </member>
+ <member name="editor/movie_writer/movie_file" type="String" setter="" getter="" default="&quot;&quot;">
+ The output path for the movie. The file extension determines the [MovieWriter] that will be used.
+ Godot has 2 built-in [MovieWriter]s:
+ - AVI container with MJPEG for video and uncompressed audio ([code].avi[/code] file extension). Lossy compression, medium file sizes, fast encoding. The lossy compression quality can be adjusted by changing [member ProjectSettings.editor/movie_writer/mjpeg_quality]. The resulting file can be viewed in most video players, but it must be converted to another format for viewing on the web or by Godot with [VideoStreamPlayer]. AVI output is currently limited to a file of 4 GB in size at most.
+ - PNG image sequence for video and WAV for audio ([code].png[/code] file extension). Lossless compression, large file sizes, slow encoding. Designed to be encoded to a video file with another tool such as [url=https://ffmpeg.org/]FFmpeg[/url] after recording. Transparency is currently not supported.
+ If you need to encode to a different format or pipe a stream through third-party software, you can extend this [MovieWriter] class to create your own movie writers.
+ When using PNG output, the frame number will be appended at the end of the file name. It starts from 0 and is padded with 8 digits to ensure correct sorting and easier processing. For example, if the output path is [code]/tmp/hello.png[/code], the first two frames will be [code]/tmp/hello00000000.png[/code] and [code]/tmp/hello00000001.png[/code]. The audio will be saved at [code]/tmp/hello.wav[/code].
+ </member>
+ <member name="editor/movie_writer/speaker_mode" type="int" setter="" getter="" default="0">
+ The speaker mode to use in the recorded audio when writing a movie. See [enum AudioServer.SpeakerMode] for possible values.
+ </member>
<member name="editor/node_naming/name_casing" type="int" setter="" getter="" default="0">
When creating node names automatically, set the type of casing in this project. This is mostly an editor setting.
</member>
diff --git a/doc/classes/StandardMaterial3D.xml b/doc/classes/StandardMaterial3D.xml
index 2305a9d325..bd6e5cdfa2 100644
--- a/doc/classes/StandardMaterial3D.xml
+++ b/doc/classes/StandardMaterial3D.xml
@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="StandardMaterial3D" inherits="BaseMaterial3D" version="4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
+ Physically based rendering (PBR) material that can be applied to 3D objects.
</brief_description>
<description>
+ StandardMaterial3D's properties are inherited from [BaseMaterial3D].
</description>
<tutorials>
- <link title="Standard Material 3D">$DOCS_URL/tutorials/3d/standard_material_3d.html</link>
+ <link title="Standard Material 3D and ORM Material 3D">$DOCS_URL/tutorials/3d/standard_material_3d.html</link>
</tutorials>
</class>
diff --git a/doc/classes/TileData.xml b/doc/classes/TileData.xml
index 0c2961fd5e..66cf602e5d 100644
--- a/doc/classes/TileData.xml
+++ b/doc/classes/TileData.xml
@@ -199,7 +199,8 @@
</member>
<member name="flip_v" type="bool" setter="set_flip_v" getter="get_flip_v" default="false">
</member>
- <member name="material" type="ShaderMaterial" setter="set_material" getter="get_material">
+ <member name="material" type="Material" setter="set_material" getter="get_material">
+ The [Material] to use for this [TileData]. This can be a [CanvasItemMaterial] to use the default shader, or a [ShaderMaterial] to use a custom shader.
</member>
<member name="modulate" type="Color" setter="set_modulate" getter="get_modulate" default="Color(1, 1, 1, 1)">
</member>
diff --git a/doc/tools/make_rst.py b/doc/tools/make_rst.py
index f59f2ff872..ce09361dfa 100755
--- a/doc/tools/make_rst.py
+++ b/doc/tools/make_rst.py
@@ -913,9 +913,9 @@ def format_codeblock(code_type, post_text, indent_level, state): # types: str,
if to_skip > indent_level:
print_error(
- "{}.xml: Four spaces should be used for indentation within ["
- + code_type
- + "].".format(state.current_class),
+ "{}.xml: Four spaces should be used for indentation within [{}].".format(
+ state.current_class, code_type
+ ),
state,
)
diff --git a/drivers/gles3/SCsub b/drivers/gles3/SCsub
index 5760fd714e..506312df80 100644
--- a/drivers/gles3/SCsub
+++ b/drivers/gles3/SCsub
@@ -7,3 +7,4 @@ env.add_source_files(env.drivers_sources, "*.cpp")
SConscript("shaders/SCsub")
SConscript("storage/SCsub")
SConscript("effects/SCsub")
+SConscript("environment/SCsub")
diff --git a/drivers/gles3/environment/SCsub b/drivers/gles3/environment/SCsub
new file mode 100644
index 0000000000..91e1140b75
--- /dev/null
+++ b/drivers/gles3/environment/SCsub
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env.add_source_files(env.drivers_sources, "*.cpp")
diff --git a/drivers/gles3/environment/gi.cpp b/drivers/gles3/environment/gi.cpp
new file mode 100644
index 0000000000..98d698b2ae
--- /dev/null
+++ b/drivers/gles3/environment/gi.cpp
@@ -0,0 +1,140 @@
+/*************************************************************************/
+/* gi.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifdef GLES3_ENABLED
+
+#include "gi.h"
+
+using namespace GLES3;
+
+/* VOXEL GI API */
+
+RID GI::voxel_gi_allocate() {
+ return RID();
+}
+
+void GI::voxel_gi_free(RID p_rid) {
+}
+
+void GI::voxel_gi_initialize(RID p_rid) {
+}
+
+void GI::voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) {
+}
+
+AABB GI::voxel_gi_get_bounds(RID p_voxel_gi) const {
+ return AABB();
+}
+
+Vector3i GI::voxel_gi_get_octree_size(RID p_voxel_gi) const {
+ return Vector3i();
+}
+
+Vector<uint8_t> GI::voxel_gi_get_octree_cells(RID p_voxel_gi) const {
+ return Vector<uint8_t>();
+}
+
+Vector<uint8_t> GI::voxel_gi_get_data_cells(RID p_voxel_gi) const {
+ return Vector<uint8_t>();
+}
+
+Vector<uint8_t> GI::voxel_gi_get_distance_field(RID p_voxel_gi) const {
+ return Vector<uint8_t>();
+}
+
+Vector<int> GI::voxel_gi_get_level_counts(RID p_voxel_gi) const {
+ return Vector<int>();
+}
+
+Transform3D GI::voxel_gi_get_to_cell_xform(RID p_voxel_gi) const {
+ return Transform3D();
+}
+
+void GI::voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) {
+}
+
+float GI::voxel_gi_get_dynamic_range(RID p_voxel_gi) const {
+ return 0;
+}
+
+void GI::voxel_gi_set_propagation(RID p_voxel_gi, float p_range) {
+}
+
+float GI::voxel_gi_get_propagation(RID p_voxel_gi) const {
+ return 0;
+}
+
+void GI::voxel_gi_set_energy(RID p_voxel_gi, float p_range) {
+}
+
+float GI::voxel_gi_get_energy(RID p_voxel_gi) const {
+ return 0.0;
+}
+
+void GI::voxel_gi_set_bias(RID p_voxel_gi, float p_range) {
+}
+
+float GI::voxel_gi_get_bias(RID p_voxel_gi) const {
+ return 0.0;
+}
+
+void GI::voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) {
+}
+
+float GI::voxel_gi_get_normal_bias(RID p_voxel_gi) const {
+ return 0.0;
+}
+
+void GI::voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) {
+}
+
+bool GI::voxel_gi_is_interior(RID p_voxel_gi) const {
+ return false;
+}
+
+void GI::voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) {
+}
+
+bool GI::voxel_gi_is_using_two_bounces(RID p_voxel_gi) const {
+ return false;
+}
+
+void GI::voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) {
+}
+
+float GI::voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const {
+ return 0;
+}
+
+uint32_t GI::voxel_gi_get_version(RID p_voxel_gi) const {
+ return 0;
+}
+
+#endif // GLES3_ENABLED
diff --git a/drivers/gles3/environment/gi.h b/drivers/gles3/environment/gi.h
new file mode 100644
index 0000000000..bff482d7fa
--- /dev/null
+++ b/drivers/gles3/environment/gi.h
@@ -0,0 +1,99 @@
+/*************************************************************************/
+/* gi.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GI_GLES3_H
+#define GI_GLES3_H
+
+#ifdef GLES3_ENABLED
+
+#include "core/templates/local_vector.h"
+#include "core/templates/rid_owner.h"
+#include "core/templates/self_list.h"
+#include "servers/rendering/environment/renderer_gi.h"
+
+#include "platform_config.h"
+#ifndef OPENGL_INCLUDE_H
+#include <GLES3/gl3.h>
+#else
+#include OPENGL_INCLUDE_H
+#endif
+
+namespace GLES3 {
+
+class GI : public RendererGI {
+public:
+ /* VOXEL GI API */
+
+ virtual RID voxel_gi_allocate() override;
+ virtual void voxel_gi_free(RID p_rid) override;
+ virtual void voxel_gi_initialize(RID p_rid) override;
+ virtual void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) override;
+
+ virtual AABB voxel_gi_get_bounds(RID p_voxel_gi) const override;
+ virtual Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const override;
+ virtual Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const override;
+ virtual Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const override;
+ virtual Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const override;
+
+ virtual Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const override;
+ virtual Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) override;
+ virtual float voxel_gi_get_dynamic_range(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) override;
+ virtual float voxel_gi_get_propagation(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_range) override;
+ virtual float voxel_gi_get_energy(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_range) override;
+ virtual float voxel_gi_get_bias(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) override;
+ virtual float voxel_gi_get_normal_bias(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) override;
+ virtual bool voxel_gi_is_interior(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) override;
+ virtual bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) override;
+ virtual float voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const override;
+
+ virtual uint32_t voxel_gi_get_version(RID p_voxel_gi) const override;
+};
+
+}; // namespace GLES3
+
+#endif // GLES3_ENABLED
+
+#endif // !GI_GLES3_H
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index c8705dc8c8..78ffb42557 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -198,6 +198,8 @@ void RasterizerGLES3::finalize() {
memdelete(scene);
memdelete(canvas);
memdelete(storage);
+ memdelete(gi);
+ memdelete(copy_effects);
memdelete(light_storage);
memdelete(particles_storage);
memdelete(mesh_storage);
@@ -269,6 +271,7 @@ RasterizerGLES3::RasterizerGLES3() {
particles_storage = memnew(GLES3::ParticlesStorage);
light_storage = memnew(GLES3::LightStorage);
copy_effects = memnew(GLES3::CopyEffects);
+ gi = memnew(GLES3::GI);
storage = memnew(RasterizerStorageGLES3);
canvas = memnew(RasterizerCanvasGLES3(storage));
scene = memnew(RasterizerSceneGLES3(storage));
diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h
index 5f1cbab849..c0322dc45b 100644
--- a/drivers/gles3/rasterizer_gles3.h
+++ b/drivers/gles3/rasterizer_gles3.h
@@ -34,6 +34,7 @@
#ifdef GLES3_ENABLED
#include "effects/copy_effects.h"
+#include "environment/gi.h"
#include "rasterizer_canvas_gles3.h"
#include "rasterizer_scene_gles3.h"
#include "rasterizer_storage_gles3.h"
@@ -59,6 +60,7 @@ protected:
GLES3::MeshStorage *mesh_storage = nullptr;
GLES3::ParticlesStorage *particles_storage = nullptr;
GLES3::LightStorage *light_storage = nullptr;
+ GLES3::GI *gi = nullptr;
GLES3::CopyEffects *copy_effects = nullptr;
RasterizerStorageGLES3 *storage = nullptr;
RasterizerCanvasGLES3 *canvas = nullptr;
@@ -72,6 +74,7 @@ public:
RendererMeshStorage *get_mesh_storage() { return mesh_storage; }
RendererParticlesStorage *get_particles_storage() { return particles_storage; }
RendererTextureStorage *get_texture_storage() { return texture_storage; }
+ RendererGI *get_gi() { return gi; }
RendererStorage *get_storage() { return storage; }
RendererCanvasRender *get_canvas() { return canvas; }
RendererSceneRender *get_scene() { return scene; }
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index bbe4d92856..3b80d88666 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -79,106 +79,6 @@ Vector<uint8_t> RasterizerStorageGLES3::buffer_get_data(GLenum p_target, GLuint
return ret;
}
-/* VOXEL GI API */
-
-RID RasterizerStorageGLES3::voxel_gi_allocate() {
- return RID();
-}
-
-void RasterizerStorageGLES3::voxel_gi_initialize(RID p_rid) {
-}
-
-void RasterizerStorageGLES3::voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) {
-}
-
-AABB RasterizerStorageGLES3::voxel_gi_get_bounds(RID p_voxel_gi) const {
- return AABB();
-}
-
-Vector3i RasterizerStorageGLES3::voxel_gi_get_octree_size(RID p_voxel_gi) const {
- return Vector3i();
-}
-
-Vector<uint8_t> RasterizerStorageGLES3::voxel_gi_get_octree_cells(RID p_voxel_gi) const {
- return Vector<uint8_t>();
-}
-
-Vector<uint8_t> RasterizerStorageGLES3::voxel_gi_get_data_cells(RID p_voxel_gi) const {
- return Vector<uint8_t>();
-}
-
-Vector<uint8_t> RasterizerStorageGLES3::voxel_gi_get_distance_field(RID p_voxel_gi) const {
- return Vector<uint8_t>();
-}
-
-Vector<int> RasterizerStorageGLES3::voxel_gi_get_level_counts(RID p_voxel_gi) const {
- return Vector<int>();
-}
-
-Transform3D RasterizerStorageGLES3::voxel_gi_get_to_cell_xform(RID p_voxel_gi) const {
- return Transform3D();
-}
-
-void RasterizerStorageGLES3::voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) {
-}
-
-float RasterizerStorageGLES3::voxel_gi_get_dynamic_range(RID p_voxel_gi) const {
- return 0;
-}
-
-void RasterizerStorageGLES3::voxel_gi_set_propagation(RID p_voxel_gi, float p_range) {
-}
-
-float RasterizerStorageGLES3::voxel_gi_get_propagation(RID p_voxel_gi) const {
- return 0;
-}
-
-void RasterizerStorageGLES3::voxel_gi_set_energy(RID p_voxel_gi, float p_range) {
-}
-
-float RasterizerStorageGLES3::voxel_gi_get_energy(RID p_voxel_gi) const {
- return 0.0;
-}
-
-void RasterizerStorageGLES3::voxel_gi_set_bias(RID p_voxel_gi, float p_range) {
-}
-
-float RasterizerStorageGLES3::voxel_gi_get_bias(RID p_voxel_gi) const {
- return 0.0;
-}
-
-void RasterizerStorageGLES3::voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) {
-}
-
-float RasterizerStorageGLES3::voxel_gi_get_normal_bias(RID p_voxel_gi) const {
- return 0.0;
-}
-
-void RasterizerStorageGLES3::voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) {
-}
-
-bool RasterizerStorageGLES3::voxel_gi_is_interior(RID p_voxel_gi) const {
- return false;
-}
-
-void RasterizerStorageGLES3::voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) {
-}
-
-bool RasterizerStorageGLES3::voxel_gi_is_using_two_bounces(RID p_voxel_gi) const {
- return false;
-}
-
-void RasterizerStorageGLES3::voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) {
-}
-
-float RasterizerStorageGLES3::voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const {
- return 0;
-}
-
-uint32_t RasterizerStorageGLES3::voxel_gi_get_version(RID p_voxel_gi) {
- return 0;
-}
-
/* OCCLUDER */
void RasterizerStorageGLES3::occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) {
diff --git a/drivers/gles3/rasterizer_storage_gles3.h b/drivers/gles3/rasterizer_storage_gles3.h
index 981080f6a5..c42efbce19 100644
--- a/drivers/gles3/rasterizer_storage_gles3.h
+++ b/drivers/gles3/rasterizer_storage_gles3.h
@@ -154,47 +154,6 @@ public:
public:
virtual void base_update_dependency(RID p_base, DependencyTracker *p_instance) override;
- /* VOXEL GI API */
-
- RID voxel_gi_allocate() override;
- void voxel_gi_initialize(RID p_rid) override;
- void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) override;
-
- AABB voxel_gi_get_bounds(RID p_voxel_gi) const override;
- Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const override;
- Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const override;
- Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const override;
- Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const override;
-
- Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const override;
- Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const override;
-
- void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) override;
- float voxel_gi_get_dynamic_range(RID p_voxel_gi) const override;
-
- void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) override;
- float voxel_gi_get_propagation(RID p_voxel_gi) const override;
-
- void voxel_gi_set_energy(RID p_voxel_gi, float p_range) override;
- float voxel_gi_get_energy(RID p_voxel_gi) const override;
-
- void voxel_gi_set_bias(RID p_voxel_gi, float p_range) override;
- float voxel_gi_get_bias(RID p_voxel_gi) const override;
-
- void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) override;
- float voxel_gi_get_normal_bias(RID p_voxel_gi) const override;
-
- void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) override;
- bool voxel_gi_is_interior(RID p_voxel_gi) const override;
-
- void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) override;
- bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const override;
-
- void voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) override;
- float voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const override;
-
- uint32_t voxel_gi_get_version(RID p_voxel_gi) override;
-
/* OCCLUDER */
void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices);
diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp
index 77aab72d40..99aa0e2442 100644
--- a/drivers/vulkan/rendering_device_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_vulkan.cpp
@@ -2314,6 +2314,12 @@ RID RenderingDeviceVulkan::texture_create_shared_from_slice(const TextureView &p
image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
}
+ if (p_slice_type == TEXTURE_SLICE_2D) {
+ texture.type = TEXTURE_TYPE_2D;
+ } else if (p_slice_type == TEXTURE_SLICE_3D) {
+ texture.type = TEXTURE_TYPE_3D;
+ }
+
if (p_view.format_override == DATA_FORMAT_MAX || p_view.format_override == texture.format) {
image_view_create_info.format = vulkan_formats[texture.format];
} else {
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 15770f9ac8..bcfc516849 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -2341,6 +2341,20 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
return;
}
+ String write_movie_file;
+ if (write_movie_button->is_pressed()) {
+ if (p_current && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->has_meta("movie_file")) {
+ // If the scene file has a movie_file metadata set, use this as file. Quick workaround if you want to have multiple scenes that write to multiple movies.
+ write_movie_file = get_tree()->get_edited_scene_root()->get_meta("movie_file");
+ } else {
+ write_movie_file = GLOBAL_GET("editor/movie_writer/movie_file");
+ }
+ if (write_movie_file == String()) {
+ show_accept(TTR("Movie Maker mode is enabled, but no movie file path has been specified.\nA default movie file path can be specified in the project settings under the 'Editor/Movie Writer' category.\nAlternatively, for running single scenes, a 'movie_path' metadata can be added to the root node,\nspecifying the path to a movie file that will be used when recording that scene."), TTR("OK"));
+ return;
+ }
+ }
+
play_button->set_pressed(false);
play_button->set_icon(gui_base->get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
play_scene_button->set_pressed(false);
@@ -2404,7 +2418,7 @@ void EditorNode::_run(bool p_current, const String &p_custom) {
}
EditorDebuggerNode::get_singleton()->start();
- Error error = editor_run.run(run_filename);
+ Error error = editor_run.run(run_filename, write_movie_file);
if (error != OK) {
EditorDebuggerNode::get_singleton()->stop();
show_accept(TTR("Could not start subprocess(es)!"), TTR("OK"));
@@ -2787,6 +2801,9 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
case RUN_SETTINGS: {
project_settings_editor->popup_project_settings();
} break;
+ case RUN_WRITE_MOVIE: {
+ _update_write_movie_icon();
+ } break;
case FILE_INSTALL_ANDROID_SOURCE: {
if (p_confirmed) {
export_template_manager->install_android_template();
@@ -4948,6 +4965,14 @@ String EditorNode::get_run_playing_scene() const {
return run_filename;
}
+void EditorNode::_update_write_movie_icon() {
+ if (write_movie_button->is_pressed()) {
+ write_movie_button->set_icon(gui_base->get_theme_icon(SNAME("MainMovieWriteEnabled"), SNAME("EditorIcons")));
+ } else {
+ write_movie_button->set_icon(gui_base->get_theme_icon(SNAME("MainMovieWrite"), SNAME("EditorIcons")));
+ }
+}
+
void EditorNode::_immediate_dialog_confirmed() {
immediate_dialog_confirmed = true;
}
@@ -6703,6 +6728,23 @@ EditorNode::EditorNode() {
ED_SHORTCUT_OVERRIDE("editor/play_custom_scene", "macos", KeyModifierMask::CMD | KeyModifierMask::SHIFT | Key::R);
play_custom_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/play_custom_scene"));
+ write_movie_button = memnew(Button);
+ write_movie_button->set_flat(true);
+ write_movie_button->set_toggle_mode(true);
+ play_hb->add_child(write_movie_button);
+ write_movie_button->set_pressed(false);
+ write_movie_button->set_icon(gui_base->get_theme_icon(SNAME("MainMovieWrite"), SNAME("EditorIcons")));
+ write_movie_button->set_focus_mode(Control::FOCUS_NONE);
+ write_movie_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option), make_binds(RUN_WRITE_MOVIE));
+ write_movie_button->set_tooltip(TTR("Enable Movie Maker mode.\nThe project will run at stable FPS and the visual and audio output will be recorded to a video file."));
+ // Restore these values to something more useful so it ignores the theme
+ write_movie_button->add_theme_color_override("icon_normal_color", Color(1, 1, 1, 0.4));
+ write_movie_button->add_theme_color_override("icon_pressed_color", Color(1, 1, 1, 1));
+ write_movie_button->add_theme_color_override("icon_hover_color", Color(1.2, 1.2, 1.2, 0.4));
+ write_movie_button->add_theme_color_override("icon_hover_pressed_color", Color(1.2, 1.2, 1.2, 1));
+ write_movie_button->add_theme_color_override("icon_focus_color", Color(1, 1, 1, 1));
+ write_movie_button->add_theme_color_override("icon_disabled_color", Color(1, 1, 1, 0.4));
+
HBoxContainer *right_menu_hb = memnew(HBoxContainer);
menu_hb->add_child(right_menu_hb);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 48df767562..89f80baeb9 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -173,6 +173,7 @@ private:
RUN_PLAY_CUSTOM_SCENE,
RUN_SETTINGS,
RUN_USER_DATA_FOLDER,
+ RUN_WRITE_MOVIE,
RELOAD_CURRENT_PROJECT,
RUN_PROJECT_MANAGER,
RUN_VCS_METADATA,
@@ -333,6 +334,7 @@ private:
Button *play_scene_button = nullptr;
Button *play_custom_scene_button = nullptr;
Button *search_button = nullptr;
+ Button *write_movie_button = nullptr;
TextureProgressBar *audio_vu = nullptr;
Timer *screenshot_timer = nullptr;
@@ -667,7 +669,7 @@ private:
void _pick_main_scene_custom_action(const String &p_custom_action_name);
void _immediate_dialog_confirmed();
-
+ void _update_write_movie_icon();
void _select_default_main_screen_plugin();
void _bottom_panel_switch(bool p_enable, int p_idx);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index a5c02c70d9..61b434a240 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -3747,11 +3747,11 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
EditorPropertyLocale *editor = memnew(EditorPropertyLocale);
editor->setup(p_hint_text);
return editor;
- } else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) {
+ } else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) {
Vector<String> extensions = p_hint_text.split(",");
- bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE;
+ bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE || p_hint == PROPERTY_HINT_GLOBAL_SAVE_FILE;
bool folder = p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_GLOBAL_DIR;
- bool save = p_hint == PROPERTY_HINT_SAVE_FILE;
+ bool save = p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_SAVE_FILE;
EditorPropertyPath *editor = memnew(EditorPropertyPath);
editor->setup(extensions, folder, global);
if (save) {
diff --git a/editor/editor_run.cpp b/editor/editor_run.cpp
index 6a2ff50ee0..04a3bf2915 100644
--- a/editor/editor_run.cpp
+++ b/editor/editor_run.cpp
@@ -43,7 +43,7 @@ String EditorRun::get_running_scene() const {
return running_scene;
}
-Error EditorRun::run(const String &p_scene) {
+Error EditorRun::run(const String &p_scene, const String &p_write_movie) {
List<String> args;
String resource_path = ProjectSettings::get_singleton()->get_resource_path();
@@ -68,6 +68,16 @@ Error EditorRun::run(const String &p_scene) {
args.push_back("--debug-navigation");
}
+ if (p_write_movie != "") {
+ args.push_back("--write-movie");
+ args.push_back(p_write_movie);
+ args.push_back("--fixed-fps");
+ args.push_back(itos(GLOBAL_GET("editor/movie_writer/fps")));
+ if (bool(GLOBAL_GET("editor/movie_writer/disable_vsync"))) {
+ args.push_back("--disable-vsync");
+ }
+ }
+
int screen = EditorSettings::get_singleton()->get("run/window_placement/screen");
if (screen == 0) {
// Same as editor
diff --git a/editor/editor_run.h b/editor/editor_run.h
index 50604ff032..4cbc6838e4 100644
--- a/editor/editor_run.h
+++ b/editor/editor_run.h
@@ -50,7 +50,7 @@ private:
public:
Status get_status() const;
String get_running_scene() const;
- Error run(const String &p_scene);
+ Error run(const String &p_scene, const String &p_write_movie = "");
void run_native_notify() { status = STATUS_PLAY; }
void stop();
diff --git a/editor/icons/MainMovieWrite.svg b/editor/icons/MainMovieWrite.svg
new file mode 100644
index 0000000000..21464bb57c
--- /dev/null
+++ b/editor/icons/MainMovieWrite.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 2a6 6 0 0 0-6 6 6 6 0 0 0 6 6 6 6 0 0 0 4-1.535V14h.002a2 2 0 0 0 .266 1A2 2 0 0 0 14 16h1v-2h-.5a.5.5 0 0 1-.5-.5V8a6 6 0 0 0-6-6zm0 1a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1zm3.441 2a1 1 0 0 1 .89.5 1 1 0 0 1-.366 1.365 1 1 0 0 1-1.367-.365 1 1 0 0 1 .367-1.365A1 1 0 0 1 11.44 5zm-6.953.002a1 1 0 0 1 .547.133A1 1 0 0 1 5.402 6.5a1 1 0 0 1-1.367.365A1 1 0 0 1 3.67 5.5a1 1 0 0 1 .818-.498zM4.512 9a1 1 0 0 1 .89.5 1 1 0 0 1-.367 1.365A1 1 0 0 1 3.67 10.5a1 1 0 0 1 .365-1.365A1 1 0 0 1 4.512 9zm6.904.002a1 1 0 0 1 .549.133 1 1 0 0 1 .365 1.365 1 1 0 0 1-1.365.365 1 1 0 0 1-.367-1.365 1 1 0 0 1 .818-.498zM8 11a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/MainMovieWriteEnabled.svg b/editor/icons/MainMovieWriteEnabled.svg
new file mode 100644
index 0000000000..b12ea38bed
--- /dev/null
+++ b/editor/icons/MainMovieWriteEnabled.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 2a6 6 0 0 0-6 6 6 6 0 0 0 6 6 6 6 0 0 0 4-1.535V14h.002a2 2 0 0 0 .266 1A2 2 0 0 0 14 16h1v-2h-.5a.5.5 0 0 1-.5-.5V8a6 6 0 0 0-6-6zm0 1a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1zm3.441 2a1 1 0 0 1 .89.5 1 1 0 0 1-.366 1.365 1 1 0 0 1-1.367-.365 1 1 0 0 1 .367-1.365A1 1 0 0 1 11.44 5zm-6.953.002a1 1 0 0 1 .547.133A1 1 0 0 1 5.402 6.5a1 1 0 0 1-1.367.365A1 1 0 0 1 3.67 5.5a1 1 0 0 1 .818-.498zM4.512 9a1 1 0 0 1 .89.5 1 1 0 0 1-.367 1.365A1 1 0 0 1 3.67 10.5a1 1 0 0 1 .365-1.365A1 1 0 0 1 4.512 9zm6.904.002a1 1 0 0 1 .549.133 1 1 0 0 1 .365 1.365 1 1 0 0 1-1.365.365 1 1 0 0 1-.367-1.365 1 1 0 0 1 .818-.498zM8 11a1 1 0 0 1 1 1 1 1 0 0 1-1 1 1 1 0 0 1-1-1 1 1 0 0 1 1-1z" fill="#e0e0e0" style="fill:#ee5353;fill-opacity:1"/></svg>
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index 98ccc1fdbe..e5ca5d66e8 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -1053,7 +1053,7 @@ void AnimationPlayerEditor::_animation_duplicate() {
_update_name_dialog_library_dropdown();
name_dialog_op = TOOL_DUPLICATE_ANIM;
- name_dialog->set_title("Duplicate Animation");
+ name_dialog->set_title(TTR("Duplicate Animation"));
name_title->set_text(TTR("Duplicated Animation Name:"));
name->set_text(new_name);
name_dialog->popup_centered(Size2(300, 90));
diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp
index 2ba2466646..00cc5a6ca0 100644
--- a/editor/plugins/animation_state_machine_editor.cpp
+++ b/editor/plugins/animation_state_machine_editor.cpp
@@ -817,11 +817,11 @@ bool AnimationNodeStateMachineEditor::_create_submenu(PopupMenu *p_menu, Ref<Ani
Vector<Ref<AnimationNodeStateMachine>> parents = p_parents;
if (from_root) {
- Ref<AnimationNodeStateMachine> prev = p_nodesm->get_prev_state_machine();
+ AnimationNodeStateMachine *prev = p_nodesm->get_prev_state_machine();
- while (prev.is_valid()) {
+ while (prev != nullptr) {
parents.push_back(prev);
- p_nodesm = prev;
+ p_nodesm = Ref<AnimationNodeStateMachine>(prev);
prev_path += "../";
prev = prev->get_prev_state_machine();
}
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index ec45341970..468681c967 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -1147,7 +1147,7 @@ void TileDataDefaultEditor::setup_property_editor(Variant::Type p_type, String p
property_editor = EditorInspectorDefaultPlugin::get_editor_for_property(dummy_object, p_type, p_property, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT);
property_editor->set_object_and_property(dummy_object, p_property);
if (p_label.is_empty()) {
- property_editor->set_label(p_property);
+ property_editor->set_label(EditorPropertyNameProcessor::get_singleton()->process_name(p_property, EditorPropertyNameProcessor::get_default_inspector_style()));
} else {
property_editor->set_label(p_label);
}
@@ -1173,6 +1173,7 @@ TileDataDefaultEditor::TileDataDefaultEditor() {
label = memnew(Label);
label->set_text(TTR("Painting:"));
+ label->set_theme_type_variation("HeaderSmall");
add_child(label);
toolbar->add_child(memnew(VSeparator));
@@ -2566,7 +2567,8 @@ TileDataTerrainsEditor::TileDataTerrainsEditor() {
undo_redo = EditorNode::get_undo_redo();
label = memnew(Label);
- label->set_text("Painting:");
+ label->set_text(TTR("Painting:"));
+ label->set_theme_type_variation("HeaderSmall");
add_child(label);
// Toolbar
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 66459d3ef9..37ccc6ad45 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -97,9 +97,9 @@ bool TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get(const StringN
void TileSetAtlasSourceEditor::TileSetAtlasSourceProxyObject::_get_property_list(List<PropertyInfo> *p_list) const {
p_list->push_back(PropertyInfo(Variant::STRING, "name", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"));
- p_list->push_back(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, ""));
- p_list->push_back(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, ""));
- p_list->push_back(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "margins", PROPERTY_HINT_NONE, "suffix:px"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "separation", PROPERTY_HINT_NONE, "suffix:px"));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "texture_region_size", PROPERTY_HINT_NONE, "suffix:px"));
p_list->push_back(PropertyInfo(Variant::BOOL, "use_texture_padding", PROPERTY_HINT_NONE, ""));
}
@@ -401,15 +401,15 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro
if (all_alternatve_id_zero) {
p_list->push_back(PropertyInfo(Variant::NIL, "Animation", PROPERTY_HINT_NONE, "animation_", PROPERTY_USAGE_GROUP));
p_list->push_back(PropertyInfo(Variant::INT, "animation_columns", PROPERTY_HINT_NONE, ""));
- p_list->push_back(PropertyInfo(Variant::VECTOR2I, "animation_separation", PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::VECTOR2I, "animation_separation", PROPERTY_HINT_NONE, "suffix:px"));
p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_speed", PROPERTY_HINT_NONE, ""));
p_list->push_back(PropertyInfo(Variant::INT, "animation_frames_count", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_ARRAY, "Frames,animation_frame_"));
// Not optimal, but returns value for the first tile. This is similar to what MultiNodeEdit does.
if (tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile) == 1) {
- p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_frame_0/duration", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, "animation_frame_0/duration", PROPERTY_HINT_NONE, "suffix:s", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY));
} else {
for (int i = 0; i < tile_set_atlas_source->get_tile_animation_frames_count(tiles.front()->get().tile); i++) {
- p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, ""));
+ p_list->push_back(PropertyInfo(Variant::FLOAT, vformat("animation_frame_%d/duration", i), PROPERTY_HINT_NONE, "suffix:s"));
}
}
}
@@ -2335,6 +2335,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
// Tile inspector.
tile_inspector_label = memnew(Label);
tile_inspector_label->set_text(TTR("Tile Properties:"));
+ tile_inspector_label->set_theme_type_variation("HeaderSmall");
middle_vbox_container->add_child(tile_inspector_label);
tile_proxy_object = memnew(AtlasTileProxyObject(this));
@@ -2350,7 +2351,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_inspector_no_tile_selected_label = memnew(Label);
tile_inspector_no_tile_selected_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- tile_inspector_no_tile_selected_label->set_text(TTR("No tile selected."));
+ tile_inspector_no_tile_selected_label->set_text(TTR("No tiles selected."));
middle_vbox_container->add_child(tile_inspector_no_tile_selected_label);
// Property values palette.
@@ -2358,6 +2359,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
tile_data_editors_label = memnew(Label);
tile_data_editors_label->set_text(TTR("Paint Properties:"));
+ tile_data_editors_label->set_theme_type_variation("HeaderSmall");
middle_vbox_container->add_child(tile_data_editors_label);
tile_data_editor_dropdown_button = memnew(Button);
@@ -2381,6 +2383,7 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
// Atlas source inspector.
atlas_source_inspector_label = memnew(Label);
atlas_source_inspector_label->set_text(TTR("Atlas Properties:"));
+ atlas_source_inspector_label->set_theme_type_variation("HeaderSmall");
middle_vbox_container->add_child(atlas_source_inspector_label);
atlas_source_proxy_object = memnew(TileSetAtlasSourceProxyObject());
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index 404199d2da..1524993bd0 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -35,6 +35,7 @@
#include "editor/editor_log.h"
#include "editor/editor_node.h"
#include "editor/editor_scale.h"
+#include "servers/movie_writer/movie_writer.h"
ProjectSettingsEditor *ProjectSettingsEditor::singleton = nullptr;
@@ -261,6 +262,7 @@ void ProjectSettingsEditor::_add_feature_overrides() {
presets.insert("standalone");
presets.insert("32");
presets.insert("64");
+ presets.insert("movie");
EditorExport *ee = EditorExport::get_singleton();
@@ -698,4 +700,6 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
import_defaults_editor->set_name(TTR("Import Defaults"));
tab_container->add_child(import_defaults_editor);
import_defaults_editor->connect("project_settings_changed", callable_mp(this, &ProjectSettingsEditor::queue_save));
+
+ MovieWriter::set_extensions_hint(); // ensure extensions are properly displayed.
}
diff --git a/editor/scene_tree_editor.cpp b/editor/scene_tree_editor.cpp
index 5536e09da7..86fa9222c0 100644
--- a/editor/scene_tree_editor.cpp
+++ b/editor/scene_tree_editor.cpp
@@ -1353,8 +1353,9 @@ void SceneTreeDialog::_cancel() {
void SceneTreeDialog::_select() {
if (tree->get_selected()) {
- emit_signal(SNAME("selected"), tree->get_selected()->get_path());
+ // The signal may cause another dialog to be displayed, so be sure to hide this one first.
hide();
+ emit_signal(SNAME("selected"), tree->get_selected()->get_path());
}
}
diff --git a/main/main.cpp b/main/main.cpp
index 50add16f3d..bfb0eacdfc 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -65,6 +65,8 @@
#include "servers/audio_server.h"
#include "servers/camera_server.h"
#include "servers/display_server.h"
+#include "servers/movie_writer/movie_writer.h"
+#include "servers/movie_writer/movie_writer_mjpeg.h"
#include "servers/navigation_server_2d.h"
#include "servers/navigation_server_3d.h"
#include "servers/physics_server_2d.h"
@@ -178,6 +180,9 @@ static bool debug_navigation = false;
static int frame_delay = 0;
static bool disable_render_loop = false;
static int fixed_fps = -1;
+static String write_movie_path;
+static MovieWriter *movie_writer = nullptr;
+static bool disable_vsync = false;
static bool print_fps = false;
#ifdef TOOLS_ENABLED
static bool dump_extension_api = false;
@@ -325,6 +330,8 @@ void Main::print_help(const char *p_binary) {
OS::get_singleton()->print(" --text-driver <driver> Text driver (Fonts, BiDi, shaping)\n");
OS::get_singleton()->print(" --tablet-driver <driver> Pen tablet input driver.\n");
OS::get_singleton()->print(" --headless Enable headless mode (--display-driver headless --audio-driver Dummy). Useful for servers and with --script.\n");
+ OS::get_singleton()->print(" --write-movie <file> Run the engine in a way that a movie is written (by default .avi MJPEG). Fixed FPS is forced when enabled, but can be used to change movie FPS. Disabling vsync can speed up movie writing but makes interaction more difficult.\n");
+ OS::get_singleton()->print(" --disable-vsync Force disabling of vsync. Run the engine in a way that a movie is written (by default .avi MJPEG). Fixed FPS is forced when enabled, but can be used to change movie FPS.\n");
OS::get_singleton()->print("\n");
@@ -1136,6 +1143,20 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->print("Missing fixed-fps argument, aborting.\n");
goto error;
}
+ } else if (I->get() == "--write-movie") {
+ if (I->next()) {
+ write_movie_path = I->next()->get();
+ N = I->next()->next();
+ if (fixed_fps == -1) {
+ fixed_fps = 60;
+ }
+ OS::get_singleton()->_writing_movie = true;
+ } else {
+ OS::get_singleton()->print("Missing write-movie argument, aborting.\n");
+ goto error;
+ }
+ } else if (I->get() == "--disable-vsync") {
+ disable_vsync = true;
} else if (I->get() == "--print-fps") {
print_fps = true;
} else if (I->get() == "--profile-gpu") {
@@ -1462,7 +1483,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
if (audio_driver_idx < 0) {
- audio_driver_idx = 0;
+ audio_driver_idx = 0; // 0 Is always available as the dummy driver (no sound)
+ }
+
+ if (write_movie_path != String()) {
+ // Always use dummy driver for audio driver (which is last), also in no threaded mode.
+ audio_driver_idx = AudioDriverManager::get_driver_count() - 1;
+ AudioDriverDummy::get_dummy_singleton()->set_use_threads(false);
}
{
@@ -1470,6 +1497,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
}
{
window_vsync_mode = DisplayServer::VSyncMode(int(GLOBAL_DEF("display/window/vsync/vsync_mode", DisplayServer::VSyncMode::VSYNC_ENABLED)));
+ if (disable_vsync) {
+ window_vsync_mode = DisplayServer::VSyncMode::VSYNC_DISABLED;
+ }
}
Engine::get_singleton()->set_physics_ticks_per_second(GLOBAL_DEF_BASIC("physics/common/physics_ticks_per_second", 60));
ProjectSettings::get_singleton()->set_custom_property_info("physics/common/physics_ticks_per_second",
@@ -1553,6 +1583,7 @@ error:
display_driver = "";
audio_driver = "";
tablet_driver = "";
+ write_movie_path = "";
project_path = "";
args.clear();
@@ -1725,6 +1756,14 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
rendering_server->set_print_gpu_profile(true);
}
+ if (write_movie_path != String()) {
+ movie_writer = MovieWriter::find_writer_for_file(write_movie_path);
+ if (movie_writer == nullptr) {
+ ERR_PRINT("Can't find movie writer for file type, aborting: " + write_movie_path);
+ write_movie_path = String();
+ }
+ }
+
#ifdef UNIX_ENABLED
// Print warning after initializing the renderer but before initializing audio.
if (OS::get_singleton()->get_environment("USER") == "root" && !OS::get_singleton()->has_environment("GODOT_SILENCE_ROOT_WARNING")) {
@@ -2650,6 +2689,9 @@ bool Main::start() {
OS::get_singleton()->set_main_loop(main_loop);
+ if (movie_writer) {
+ movie_writer->begin(DisplayServer::get_singleton()->window_get_size(), fixed_fps, write_movie_path);
+ }
return true;
}
@@ -2836,6 +2878,13 @@ bool Main::iteration() {
Input::get_singleton()->flush_buffered_events();
}
+ if (movie_writer) {
+ RID main_vp_rid = RenderingServer::get_singleton()->viewport_find_from_screen_attachment(DisplayServer::MAIN_WINDOW_ID);
+ RID main_vp_texture = RenderingServer::get_singleton()->viewport_get_texture(main_vp_rid);
+ Ref<Image> vp_tex = RenderingServer::get_singleton()->texture_2d_get(main_vp_texture);
+ movie_writer->add_frame(vp_tex);
+ }
+
if (fixed_fps != -1) {
return exit;
}
@@ -2875,6 +2924,10 @@ void Main::cleanup(bool p_force) {
ERR_FAIL_COND(!_start_success);
}
+ if (movie_writer) {
+ movie_writer->end();
+ }
+
ResourceLoader::remove_custom_loaders();
ResourceSaver::remove_custom_savers();
diff --git a/modules/jpg/SCsub b/modules/jpg/SCsub
index 7c6ceeea29..b840542c1b 100644
--- a/modules/jpg/SCsub
+++ b/modules/jpg/SCsub
@@ -13,6 +13,7 @@ thirdparty_obj = []
thirdparty_dir = "#thirdparty/jpeg-compressor/"
thirdparty_sources = [
"jpgd.cpp",
+ "jpge.cpp",
]
thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
index 51358876a4..0e03fa65c8 100644
--- a/modules/jpg/image_loader_jpegd.cpp
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -33,7 +33,8 @@
#include "core/os/os.h"
#include "core/string/print_string.h"
-#include <jpgd.h>
+#include "thirdparty/jpeg-compressor/jpgd.h"
+#include "thirdparty/jpeg-compressor/jpge.h"
#include <string.h>
Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) {
@@ -131,6 +132,59 @@ static Ref<Image> _jpegd_mem_loader_func(const uint8_t *p_png, int p_size) {
return img;
}
+static Error _jpgd_save_func(const String &p_path, const Ref<Image> &p_img, float p_quality) {
+ return OK;
+}
+
+class ImageLoaderJPGOSFile : public jpge::output_stream {
+public:
+ Ref<FileAccess> f;
+
+ virtual bool put_buf(const void *Pbuf, int len) {
+ f->store_buffer((const uint8_t *)Pbuf, len);
+ return true;
+ }
+};
+
+class ImageLoaderJPGOSBuffer : public jpge::output_stream {
+public:
+ Vector<uint8_t> *buffer = nullptr;
+ virtual bool put_buf(const void *Pbuf, int len) {
+ uint32_t base = buffer->size();
+ buffer->resize(base + len);
+ memcpy(buffer->ptrw() + base, Pbuf, len);
+ return true;
+ }
+};
+
+static Vector<uint8_t> _jpgd_buffer_save_func(const Ref<Image> &p_img, float p_quality) {
+ ERR_FAIL_COND_V(p_img.is_null() || p_img->is_empty(), Vector<uint8_t>());
+ Ref<Image> image = p_img;
+ if (image->get_format() != Image::FORMAT_RGB8) {
+ image->convert(Image::FORMAT_ETC2_RGB8);
+ }
+
+ jpge::params p;
+ p.m_quality = CLAMP(p_quality * 100, 1, 100);
+ Vector<uint8_t> output;
+ ImageLoaderJPGOSBuffer ob;
+ ob.buffer = &output;
+
+ jpge::jpeg_encoder enc;
+ enc.init(&ob, image->get_width(), image->get_height(), 3, p);
+
+ const uint8_t *src_data = image->get_data().ptr();
+ for (int i = 0; i < image->get_height(); i++) {
+ enc.process_scanline(&src_data[i * image->get_width() * 3]);
+ }
+
+ enc.process_scanline(nullptr);
+
+ return output;
+}
+
ImageLoaderJPG::ImageLoaderJPG() {
Image::_jpg_mem_loader_func = _jpegd_mem_loader_func;
+ Image::save_jpg_func = _jpgd_save_func;
+ Image::save_jpg_buffer_func = _jpgd_buffer_save_func;
}
diff --git a/modules/minimp3/audio_stream_mp3.cpp b/modules/minimp3/audio_stream_mp3.cpp
index b5b51403f7..c37bea519f 100644
--- a/modules/minimp3/audio_stream_mp3.cpp
+++ b/modules/minimp3/audio_stream_mp3.cpp
@@ -38,7 +38,9 @@
#include "core/io/file_access.h"
int AudioStreamPlaybackMP3::_mix_internal(AudioFrame *p_buffer, int p_frames) {
- ERR_FAIL_COND_V(!active, 0);
+ if (!active) {
+ return 0;
+ }
int todo = p_frames;
diff --git a/modules/vorbis/audio_stream_ogg_vorbis.cpp b/modules/vorbis/audio_stream_ogg_vorbis.cpp
index b645a48c88..89a6b03ff8 100644
--- a/modules/vorbis/audio_stream_ogg_vorbis.cpp
+++ b/modules/vorbis/audio_stream_ogg_vorbis.cpp
@@ -36,23 +36,22 @@
int AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_frames) {
ERR_FAIL_COND_V(!ready, 0);
- ERR_FAIL_COND_V(!active, 0);
+
+ if (!active) {
+ return 0;
+ }
int todo = p_frames;
int start_buffer = 0;
- int frames_mixed_this_step = p_frames;
-
- while (todo && active) {
+ while (todo > 0 && active) {
AudioFrame *buffer = p_buffer;
if (start_buffer > 0) {
buffer = buffer + start_buffer;
}
int mixed = _mix_frames_vorbis(buffer, todo);
- if (mixed < 0) {
- return 0;
- }
+ ERR_FAIL_COND_V(mixed < 0, 0);
todo -= mixed;
frames_mixed += mixed;
start_buffer += mixed;
@@ -67,16 +66,14 @@ int AudioStreamPlaybackOGGVorbis::_mix_internal(AudioFrame *p_buffer, int p_fram
// we still have buffer to fill, start from this element in the next iteration.
start_buffer = p_frames - todo;
} else {
- frames_mixed_this_step = p_frames - todo;
for (int i = p_frames - todo; i < p_frames; i++) {
p_buffer[i] = AudioFrame(0, 0);
}
active = false;
- todo = 0;
}
}
}
- return frames_mixed_this_step;
+ return p_frames - todo;
}
int AudioStreamPlaybackOGGVorbis::_mix_frames_vorbis(AudioFrame *p_buffer, int p_frames) {
diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp
index 0e41f6c973..778d562278 100644
--- a/modules/webp/image_loader_webp.cpp
+++ b/modules/webp/image_loader_webp.cpp
@@ -34,184 +34,21 @@
#include "core/io/marshalls.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
+#include "webp_common.h"
#include <stdlib.h>
#include <webp/decode.h>
#include <webp/encode.h>
-static Vector<uint8_t> _webp_lossy_pack(const Ref<Image> &p_image, float p_quality) {
- ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
-
- Ref<Image> img = p_image->duplicate();
- if (img->detect_alpha()) {
- img->convert(Image::FORMAT_RGBA8);
- } else {
- img->convert(Image::FORMAT_RGB8);
- }
-
- Size2 s(img->get_width(), img->get_height());
- Vector<uint8_t> data = img->get_data();
- const uint8_t *r = data.ptr();
-
- uint8_t *dst_buff = nullptr;
- size_t dst_size = 0;
- if (img->get_format() == Image::FORMAT_RGB8) {
- dst_size = WebPEncodeRGB(r, s.width, s.height, 3 * s.width, CLAMP(p_quality * 100.0, 0, 100.0), &dst_buff);
- } else {
- dst_size = WebPEncodeRGBA(r, s.width, s.height, 4 * s.width, CLAMP(p_quality * 100.0, 0, 100.0), &dst_buff);
- }
-
- ERR_FAIL_COND_V(dst_size == 0, Vector<uint8_t>());
- Vector<uint8_t> dst;
- dst.resize(4 + dst_size);
- uint8_t *w = dst.ptrw();
- w[0] = 'W';
- w[1] = 'E';
- w[2] = 'B';
- w[3] = 'P';
- memcpy(&w[4], dst_buff, dst_size);
- WebPFree(dst_buff);
-
- return dst;
-}
-
-static Vector<uint8_t> _webp_lossless_pack(const Ref<Image> &p_image) {
- ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
-
- int compression_level = ProjectSettings::get_singleton()->get("rendering/textures/lossless_compression/webp_compression_level");
- compression_level = CLAMP(compression_level, 0, 9);
-
- Ref<Image> img = p_image->duplicate();
- if (img->detect_alpha()) {
- img->convert(Image::FORMAT_RGBA8);
- } else {
- img->convert(Image::FORMAT_RGB8);
- }
-
- Size2 s(img->get_width(), img->get_height());
- Vector<uint8_t> data = img->get_data();
- const uint8_t *r = data.ptr();
-
- // we need to use the more complex API in order to access the 'exact' flag...
-
- WebPConfig config;
- WebPPicture pic;
- if (!WebPConfigInit(&config) || !WebPConfigLosslessPreset(&config, compression_level) || !WebPPictureInit(&pic)) {
- ERR_FAIL_V(Vector<uint8_t>());
- }
-
- WebPMemoryWriter wrt;
- config.exact = 1;
- pic.use_argb = 1;
- pic.width = s.width;
- pic.height = s.height;
- pic.writer = WebPMemoryWrite;
- pic.custom_ptr = &wrt;
- WebPMemoryWriterInit(&wrt);
-
- bool success_import = false;
- if (img->get_format() == Image::FORMAT_RGB8) {
- success_import = WebPPictureImportRGB(&pic, r, 3 * s.width);
- } else {
- success_import = WebPPictureImportRGBA(&pic, r, 4 * s.width);
- }
- bool success_encode = false;
- if (success_import) {
- success_encode = WebPEncode(&config, &pic);
- }
- WebPPictureFree(&pic);
-
- if (!success_encode) {
- WebPMemoryWriterClear(&wrt);
- ERR_FAIL_V_MSG(Vector<uint8_t>(), "WebP packing failed.");
- }
-
- // copy from wrt
- Vector<uint8_t> dst;
- dst.resize(4 + wrt.size);
- uint8_t *w = dst.ptrw();
- w[0] = 'W';
- w[1] = 'E';
- w[2] = 'B';
- w[3] = 'P';
- memcpy(&w[4], wrt.mem, wrt.size);
- WebPMemoryWriterClear(&wrt);
-
- return dst;
-}
-
-static Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer) {
- int size = p_buffer.size() - 4;
- ERR_FAIL_COND_V(size <= 0, Ref<Image>());
- const uint8_t *r = p_buffer.ptr();
-
- ERR_FAIL_COND_V(r[0] != 'W' || r[1] != 'E' || r[2] != 'B' || r[3] != 'P', Ref<Image>());
- WebPBitstreamFeatures features;
- if (WebPGetFeatures(&r[4], size, &features) != VP8_STATUS_OK) {
- ERR_FAIL_V_MSG(Ref<Image>(), "Error unpacking WEBP image.");
- }
-
- /*
- print_line("width: "+itos(features.width));
- print_line("height: "+itos(features.height));
- print_line("alpha: "+itos(features.has_alpha));
- */
-
- Vector<uint8_t> dst_image;
- int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
- dst_image.resize(datasize);
-
- uint8_t *dst_w = dst_image.ptrw();
-
- bool errdec = false;
- if (features.has_alpha) {
- errdec = WebPDecodeRGBAInto(&r[4], size, dst_w, datasize, 4 * features.width) == nullptr;
- } else {
- errdec = WebPDecodeRGBInto(&r[4], size, dst_w, datasize, 3 * features.width) == nullptr;
- }
-
- ERR_FAIL_COND_V_MSG(errdec, Ref<Image>(), "Failed decoding WebP image.");
-
- Ref<Image> img = memnew(Image(features.width, features.height, 0, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image));
- return img;
-}
-
-Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) {
- ERR_FAIL_NULL_V(p_image, ERR_INVALID_PARAMETER);
-
- WebPBitstreamFeatures features;
- if (WebPGetFeatures(p_buffer, p_buffer_len, &features) != VP8_STATUS_OK) {
- ERR_FAIL_V(ERR_FILE_CORRUPT);
- }
-
- Vector<uint8_t> dst_image;
- int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
- dst_image.resize(datasize);
- uint8_t *dst_w = dst_image.ptrw();
-
- bool errdec = false;
- if (features.has_alpha) {
- errdec = WebPDecodeRGBAInto(p_buffer, p_buffer_len, dst_w, datasize, 4 * features.width) == nullptr;
- } else {
- errdec = WebPDecodeRGBInto(p_buffer, p_buffer_len, dst_w, datasize, 3 * features.width) == nullptr;
- }
-
- ERR_FAIL_COND_V_MSG(errdec, ERR_FILE_CORRUPT, "Failed decoding WebP image.");
-
- p_image->create(features.width, features.height, false, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image);
-
- return OK;
-}
-
static Ref<Image> _webp_mem_loader_func(const uint8_t *p_png, int p_size) {
Ref<Image> img;
img.instantiate();
- Error err = webp_load_image_from_buffer(img.ptr(), p_png, p_size);
+ Error err = WebPCommon::webp_load_image_from_buffer(img.ptr(), p_png, p_size);
ERR_FAIL_COND_V(err, Ref<Image>());
return img;
}
-Error ImageLoaderWEBP::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) {
+Error ImageLoaderWebP::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale) {
Vector<uint8_t> src_image;
uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
@@ -221,18 +58,18 @@ Error ImageLoaderWEBP::load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_
f->get_buffer(&w[0], src_image_len);
- Error err = webp_load_image_from_buffer(p_image.ptr(), w, src_image_len);
+ Error err = WebPCommon::webp_load_image_from_buffer(p_image.ptr(), w, src_image_len);
return err;
}
-void ImageLoaderWEBP::get_recognized_extensions(List<String> *p_extensions) const {
+void ImageLoaderWebP::get_recognized_extensions(List<String> *p_extensions) const {
p_extensions->push_back("webp");
}
-ImageLoaderWEBP::ImageLoaderWEBP() {
+ImageLoaderWebP::ImageLoaderWebP() {
Image::_webp_mem_loader_func = _webp_mem_loader_func;
- Image::webp_lossy_packer = _webp_lossy_pack;
- Image::webp_lossless_packer = _webp_lossless_pack;
- Image::webp_unpacker = _webp_unpack;
+ Image::webp_lossy_packer = WebPCommon::_webp_lossy_pack;
+ Image::webp_lossless_packer = WebPCommon::_webp_lossless_pack;
+ Image::webp_unpacker = WebPCommon::_webp_unpack;
}
diff --git a/modules/webp/image_loader_webp.h b/modules/webp/image_loader_webp.h
index 1acd1459a0..448f683eb3 100644
--- a/modules/webp/image_loader_webp.h
+++ b/modules/webp/image_loader_webp.h
@@ -33,11 +33,11 @@
#include "core/io/image_loader.h"
-class ImageLoaderWEBP : public ImageFormatLoader {
+class ImageLoaderWebP : public ImageFormatLoader {
public:
virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, bool p_force_linear, float p_scale);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
- ImageLoaderWEBP();
+ ImageLoaderWebP();
};
#endif
diff --git a/modules/webp/register_types.cpp b/modules/webp/register_types.cpp
index 148e325498..29f633743e 100644
--- a/modules/webp/register_types.cpp
+++ b/modules/webp/register_types.cpp
@@ -31,16 +31,20 @@
#include "register_types.h"
#include "image_loader_webp.h"
+#include "resource_saver_webp.h"
-static ImageLoaderWEBP *image_loader_webp = nullptr;
+static ImageLoaderWebP *image_loader_webp = nullptr;
+static Ref<ResourceSaverWebP> resource_saver_webp;
void initialize_webp_module(ModuleInitializationLevel p_level) {
if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
return;
}
- image_loader_webp = memnew(ImageLoaderWEBP);
+ image_loader_webp = memnew(ImageLoaderWebP);
+ resource_saver_webp.instantiate();
ImageLoader::add_image_format_loader(image_loader_webp);
+ ResourceSaver::add_resource_format_saver(resource_saver_webp);
}
void uninitialize_webp_module(ModuleInitializationLevel p_level) {
@@ -49,4 +53,6 @@ void uninitialize_webp_module(ModuleInitializationLevel p_level) {
}
memdelete(image_loader_webp);
+ ResourceSaver::remove_resource_format_saver(resource_saver_webp);
+ resource_saver_webp.unref();
}
diff --git a/modules/webp/resource_saver_webp.cpp b/modules/webp/resource_saver_webp.cpp
new file mode 100644
index 0000000000..d270d39163
--- /dev/null
+++ b/modules/webp/resource_saver_webp.cpp
@@ -0,0 +1,90 @@
+/*************************************************************************/
+/* resource_saver_webp.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "resource_saver_webp.h"
+
+#include "core/io/file_access.h"
+#include "core/io/image.h"
+#include "scene/resources/texture.h"
+#include "webp_common.h"
+
+Error ResourceSaverWebP::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) {
+ Ref<ImageTexture> texture = p_resource;
+
+ ERR_FAIL_COND_V_MSG(!texture.is_valid(), ERR_INVALID_PARAMETER, "Can't save invalid texture as WEBP.");
+ ERR_FAIL_COND_V_MSG(!texture->get_width(), ERR_INVALID_PARAMETER, "Can't save empty texture as WEBP.");
+
+ Ref<Image> img = texture->get_image();
+
+ Error err = save_image(p_path, img);
+
+ return err;
+}
+
+Error ResourceSaverWebP::save_image(const String &p_path, const Ref<Image> &p_img, const bool p_lossy, const float p_quality) {
+ Vector<uint8_t> buffer = save_image_to_buffer(p_img, p_lossy, p_quality);
+ Error err;
+ Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::WRITE, &err);
+ ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save WEBP at path: '%s'.", p_path));
+
+ const uint8_t *reader = buffer.ptr();
+
+ file->store_buffer(reader, buffer.size());
+ if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
+ return ERR_CANT_CREATE;
+ }
+
+ return OK;
+}
+
+Vector<uint8_t> ResourceSaverWebP::save_image_to_buffer(const Ref<Image> &p_img, const bool p_lossy, const float p_quality) {
+ Vector<uint8_t> buffer;
+ if (p_lossy) {
+ buffer = WebPCommon::_webp_lossy_pack(p_img, p_quality);
+ } else {
+ buffer = WebPCommon::_webp_lossless_pack(p_img);
+ }
+ return buffer;
+}
+
+bool ResourceSaverWebP::recognize(const Ref<Resource> &p_resource) const {
+ return (p_resource.is_valid() && p_resource->is_class("ImageTexture"));
+}
+
+void ResourceSaverWebP::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const {
+ if (Object::cast_to<ImageTexture>(*p_resource)) {
+ p_extensions->push_back("webp");
+ }
+}
+
+ResourceSaverWebP::ResourceSaverWebP() {
+ Image::save_webp_func = &save_image;
+ Image::save_webp_buffer_func = &save_image_to_buffer;
+}
diff --git a/modules/webp/resource_saver_webp.h b/modules/webp/resource_saver_webp.h
new file mode 100644
index 0000000000..59e944efa0
--- /dev/null
+++ b/modules/webp/resource_saver_webp.h
@@ -0,0 +1,49 @@
+/*************************************************************************/
+/* resource_saver_webp.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RESOURCE_SAVER_WEBP_H
+#define RESOURCE_SAVER_WEBP_H
+
+#include "core/io/image.h"
+#include "core/io/resource_saver.h"
+
+class ResourceSaverWebP : public ResourceFormatSaver {
+public:
+ static Error save_image(const String &p_path, const Ref<Image> &p_img, const bool p_lossy = false, const float p_quality = 0.75f);
+ static Vector<uint8_t> save_image_to_buffer(const Ref<Image> &p_img, const bool p_lossy = false, const float p_quality = 0.75f);
+
+ virtual Error save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags = 0);
+ virtual bool recognize(const Ref<Resource> &p_resource) const;
+ virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const;
+
+ ResourceSaverWebP();
+};
+
+#endif // RESOURCE_SAVER_WEBP_H
diff --git a/modules/webp/webp_common.cpp b/modules/webp/webp_common.cpp
new file mode 100644
index 0000000000..8657a98853
--- /dev/null
+++ b/modules/webp/webp_common.cpp
@@ -0,0 +1,190 @@
+/*************************************************************************/
+/* webp_common.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "webp_common.h"
+
+#include "core/config/project_settings.h"
+#include "core/os/os.h"
+
+#include <string.h>
+#include <webp/decode.h>
+#include <webp/encode.h>
+
+namespace WebPCommon {
+Vector<uint8_t> _webp_lossy_pack(const Ref<Image> &p_image, float p_quality) {
+ ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
+
+ Ref<Image> img = p_image->duplicate();
+ if (img->detect_alpha()) {
+ img->convert(Image::FORMAT_RGBA8);
+ } else {
+ img->convert(Image::FORMAT_RGB8);
+ }
+
+ Size2 s(img->get_width(), img->get_height());
+ Vector<uint8_t> data = img->get_data();
+ const uint8_t *r = data.ptr();
+
+ uint8_t *dst_buff = nullptr;
+ size_t dst_size = 0;
+ if (img->get_format() == Image::FORMAT_RGB8) {
+ dst_size = WebPEncodeRGB(r, s.width, s.height, 3 * s.width, CLAMP(p_quality * 100.0f, 0.0f, 100.0f), &dst_buff);
+ } else {
+ dst_size = WebPEncodeRGBA(r, s.width, s.height, 4 * s.width, CLAMP(p_quality * 100.0f, 0.0f, 100.0f), &dst_buff);
+ }
+
+ ERR_FAIL_COND_V(dst_size == 0, Vector<uint8_t>());
+ Vector<uint8_t> dst;
+ dst.resize(dst_size);
+ uint8_t *w = dst.ptrw();
+ memcpy(w, dst_buff, dst_size);
+ WebPFree(dst_buff);
+
+ return dst;
+}
+
+Vector<uint8_t> _webp_lossless_pack(const Ref<Image> &p_image) {
+ ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector<uint8_t>());
+
+ int compression_level = ProjectSettings::get_singleton()->get("rendering/textures/lossless_compression/webp_compression_level");
+ compression_level = CLAMP(compression_level, 0, 9);
+
+ Ref<Image> img = p_image->duplicate();
+ if (img->detect_alpha()) {
+ img->convert(Image::FORMAT_RGBA8);
+ } else {
+ img->convert(Image::FORMAT_RGB8);
+ }
+
+ Size2 s(img->get_width(), img->get_height());
+ Vector<uint8_t> data = img->get_data();
+ const uint8_t *r = data.ptr();
+
+ // we need to use the more complex API in order to access the 'exact' flag...
+
+ WebPConfig config;
+ WebPPicture pic;
+ if (!WebPConfigInit(&config) || !WebPConfigLosslessPreset(&config, compression_level) || !WebPPictureInit(&pic)) {
+ ERR_FAIL_V(Vector<uint8_t>());
+ }
+
+ WebPMemoryWriter wrt;
+ config.exact = 1;
+ pic.use_argb = 1;
+ pic.width = s.width;
+ pic.height = s.height;
+ pic.writer = WebPMemoryWrite;
+ pic.custom_ptr = &wrt;
+ WebPMemoryWriterInit(&wrt);
+
+ bool success_import = false;
+ if (img->get_format() == Image::FORMAT_RGB8) {
+ success_import = WebPPictureImportRGB(&pic, r, 3 * s.width);
+ } else {
+ success_import = WebPPictureImportRGBA(&pic, r, 4 * s.width);
+ }
+ bool success_encode = false;
+ if (success_import) {
+ success_encode = WebPEncode(&config, &pic);
+ }
+ WebPPictureFree(&pic);
+
+ if (!success_encode) {
+ WebPMemoryWriterClear(&wrt);
+ ERR_FAIL_V_MSG(Vector<uint8_t>(), "WebP packing failed.");
+ }
+
+ // copy from wrt
+ Vector<uint8_t> dst;
+ dst.resize(wrt.size);
+ uint8_t *w = dst.ptrw();
+ memcpy(w, wrt.mem, wrt.size);
+ WebPMemoryWriterClear(&wrt);
+ return dst;
+}
+
+Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer) {
+ int size = p_buffer.size();
+ ERR_FAIL_COND_V(size <= 0, Ref<Image>());
+ const uint8_t *r = p_buffer.ptr();
+
+ // A WebP file uses a RIFF header, which starts with "RIFF____WEBP".
+ ERR_FAIL_COND_V(r[0] != 'R' || r[1] != 'I' || r[2] != 'F' || r[3] != 'F' || r[8] != 'W' || r[9] != 'E' || r[10] != 'B' || r[11] != 'P', Ref<Image>());
+ WebPBitstreamFeatures features;
+ if (WebPGetFeatures(r, size, &features) != VP8_STATUS_OK) {
+ ERR_FAIL_V_MSG(Ref<Image>(), "Error unpacking WEBP image.");
+ }
+
+ Vector<uint8_t> dst_image;
+ int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
+ dst_image.resize(datasize);
+
+ uint8_t *dst_w = dst_image.ptrw();
+
+ bool errdec = false;
+ if (features.has_alpha) {
+ errdec = WebPDecodeRGBAInto(r, size, dst_w, datasize, 4 * features.width) == nullptr;
+ } else {
+ errdec = WebPDecodeRGBInto(r, size, dst_w, datasize, 3 * features.width) == nullptr;
+ }
+
+ ERR_FAIL_COND_V_MSG(errdec, Ref<Image>(), "Failed decoding WebP image.");
+
+ Ref<Image> img = memnew(Image(features.width, features.height, 0, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image));
+ return img;
+}
+
+Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len) {
+ ERR_FAIL_NULL_V(p_image, ERR_INVALID_PARAMETER);
+
+ WebPBitstreamFeatures features;
+ if (WebPGetFeatures(p_buffer, p_buffer_len, &features) != VP8_STATUS_OK) {
+ ERR_FAIL_V(ERR_FILE_CORRUPT);
+ }
+
+ Vector<uint8_t> dst_image;
+ int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
+ dst_image.resize(datasize);
+ uint8_t *dst_w = dst_image.ptrw();
+
+ bool errdec = false;
+ if (features.has_alpha) {
+ errdec = WebPDecodeRGBAInto(p_buffer, p_buffer_len, dst_w, datasize, 4 * features.width) == nullptr;
+ } else {
+ errdec = WebPDecodeRGBInto(p_buffer, p_buffer_len, dst_w, datasize, 3 * features.width) == nullptr;
+ }
+
+ ERR_FAIL_COND_V_MSG(errdec, ERR_FILE_CORRUPT, "Failed decoding WebP image.");
+
+ p_image->create(features.width, features.height, false, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image);
+
+ return OK;
+}
+} // namespace WebPCommon
diff --git a/modules/webp/webp_common.h b/modules/webp/webp_common.h
new file mode 100644
index 0000000000..11bef40256
--- /dev/null
+++ b/modules/webp/webp_common.h
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* webp_common.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef WEBP_COMMON_H
+#define WEBP_COMMON_H
+
+#include "core/io/image.h"
+
+namespace WebPCommon {
+// Given an image, pack this data into a WebP file.
+Vector<uint8_t> _webp_lossy_pack(const Ref<Image> &p_image, float p_quality);
+Vector<uint8_t> _webp_lossless_pack(const Ref<Image> &p_image);
+// Given a WebP file, unpack it into an image.
+Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer);
+Error webp_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p_buffer_len);
+} //namespace WebPCommon
+
+#endif // WEBP_COMMON_H
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 6d04dcdc71..9c4b0feea2 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -1090,7 +1090,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
q.occluders.clear();
// Those allow to group cell per material or z-index.
- Ref<ShaderMaterial> prev_material;
+ Ref<Material> prev_material;
int prev_z_index = 0;
RID prev_canvas_item;
@@ -1129,7 +1129,7 @@ void TileMap::_rendering_update_dirty_quadrants(SelfList<TileMapQuadrant>::List
tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile);
}
- Ref<ShaderMaterial> mat = tile_data->get_material();
+ Ref<Material> mat = tile_data->get_material();
int z_index = tile_data->get_z_index();
// Quandrant pos.
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 2c7d021427..8dcf538b8f 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -435,7 +435,7 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
// handles start_node: if previous state machine is pointing to a node inside the current state machine, starts the current machine from start_node to prev_local_to
if (p_state_machine->start_node == current && p_state_machine->transitions[i].local_from == current) {
- if (p_state_machine->prev_state_machine.is_valid()) {
+ if (p_state_machine->prev_state_machine != nullptr) {
Ref<AnimationNodeStateMachinePlayback> prev_playback = p_state_machine->prev_state_machine->get_parameter("playback");
if (prev_playback.is_valid()) {
@@ -471,9 +471,9 @@ double AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *p_s
}
if (next == p_state_machine->end_node) {
- Ref<AnimationNodeStateMachine> prev_state_machine = p_state_machine->prev_state_machine;
+ AnimationNodeStateMachine *prev_state_machine = p_state_machine->prev_state_machine;
- if (prev_state_machine.is_valid()) {
+ if (prev_state_machine != nullptr) {
Ref<AnimationNodeStateMachinePlayback> prev_playback = prev_state_machine->get_parameter("playback");
if (prev_playback.is_valid()) {
@@ -655,7 +655,7 @@ void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<Animation
if (anodesm.is_valid()) {
anodesm->state_machine_name = p_name;
- anodesm->prev_state_machine = (Ref<AnimationNodeStateMachine>)this;
+ anodesm->prev_state_machine = this;
}
emit_changed();
@@ -821,7 +821,7 @@ void AnimationNodeStateMachine::_rename_transition(const StringName &p_name, con
void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const {
List<StringName> nodes;
for (const KeyValue<StringName, State> &E : states) {
- if (E.key == end_node && !prev_state_machine.is_valid()) {
+ if (E.key == end_node && prev_state_machine == nullptr) {
continue;
}
@@ -834,7 +834,7 @@ void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const {
}
}
-Ref<AnimationNodeStateMachine> AnimationNodeStateMachine::get_prev_state_machine() const {
+AnimationNodeStateMachine *AnimationNodeStateMachine::get_prev_state_machine() const {
return prev_state_machine;
}
@@ -862,10 +862,10 @@ int AnimationNodeStateMachine::find_transition(const StringName &p_from, const S
return -1;
}
-bool AnimationNodeStateMachine::_can_connect(const StringName &p_name, Vector<Ref<AnimationNodeStateMachine>> p_parents) const {
+bool AnimationNodeStateMachine::_can_connect(const StringName &p_name, Vector<AnimationNodeStateMachine *> p_parents) {
if (p_parents.is_empty()) {
- Ref<AnimationNodeStateMachine> prev = (Ref<AnimationNodeStateMachine>)this;
- while (prev.is_valid()) {
+ AnimationNodeStateMachine *prev = this;
+ while (prev != nullptr) {
p_parents.push_back(prev);
prev = prev->prev_state_machine;
}
@@ -874,7 +874,7 @@ bool AnimationNodeStateMachine::_can_connect(const StringName &p_name, Vector<Re
if (states.has(p_name)) {
Ref<AnimationNodeStateMachine> anodesm = states[p_name].node;
- if (anodesm.is_valid() && p_parents.find(anodesm) != -1) {
+ if (anodesm.is_valid() && p_parents.find(anodesm.ptr()) != -1) {
return false;
}
@@ -889,7 +889,7 @@ bool AnimationNodeStateMachine::_can_connect(const StringName &p_name, Vector<Re
}
if (path[0] == "..") {
- if (prev_state_machine.is_valid()) {
+ if (prev_state_machine != nullptr) {
return prev_state_machine->_can_connect(name.replace_first("../", ""), p_parents);
}
} else if (states.has(path[0])) {
diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h
index 9eeac6a183..20f2d6f858 100644
--- a/scene/animation/animation_node_state_machine.h
+++ b/scene/animation/animation_node_state_machine.h
@@ -166,7 +166,7 @@ private:
StringName playback = "playback";
StringName state_machine_name;
- Ref<AnimationNodeStateMachine> prev_state_machine;
+ AnimationNodeStateMachine *prev_state_machine = nullptr;
bool updating_transitions = false;
Vector2 graph_offset;
@@ -174,7 +174,7 @@ private:
void _tree_changed();
void _remove_transition(const Ref<AnimationNodeStateMachineTransition> p_transition);
void _rename_transition(const StringName &p_name, const StringName &p_new_name);
- bool _can_connect(const StringName &p_name, const Vector<Ref<AnimationNodeStateMachine>> p_parents = Vector<Ref<AnimationNodeStateMachine>>()) const;
+ bool _can_connect(const StringName &p_name, Vector<AnimationNodeStateMachine *> p_parents = Vector<AnimationNodeStateMachine *>());
StringName _get_shortest_path(const StringName &p_path) const;
protected:
@@ -221,7 +221,7 @@ public:
bool can_edit_node(const StringName &p_name) const;
- Ref<AnimationNodeStateMachine> get_prev_state_machine() const;
+ AnimationNodeStateMachine *get_prev_state_machine() const;
void set_graph_offset(const Vector2 &p_offset);
Vector2 get_graph_offset() const;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 5d471c9e84..2afe9ac35f 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -283,6 +283,9 @@ void AnimationPlayer::_ensure_node_caches(AnimationData *p_anim, Node *p_root_ov
setup_pass++;
for (int i = 0; i < a->get_track_count(); i++) {
+ if (!a->track_is_enabled(i)) {
+ continue;
+ }
p_anim->node_cache.write[i] = nullptr;
Ref<Resource> resource;
Vector<StringName> leftover_path;
@@ -1991,8 +1994,8 @@ Ref<AnimatedValuesBackup> AnimationPlayer::apply_reset(bool p_user_initiated) {
Ref<AnimationLibrary> al;
al.instantiate();
al->add_animation(SceneStringNames::get_singleton()->RESET, reset_anim);
- aux_player->add_animation_library("default", al);
- aux_player->set_assigned_animation("default/" + SceneStringNames::get_singleton()->RESET);
+ aux_player->add_animation_library("", al);
+ aux_player->set_assigned_animation(SceneStringNames::get_singleton()->RESET);
// Forcing the use of the original root because the scene where original player belongs may be not the active one
Node *root = get_node(get_root());
Ref<AnimatedValuesBackup> old_values = aux_player->backup_animated_values(root);
diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp
index 136285c4dc..bcd49d75fa 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -276,7 +276,7 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
String new_path;
AnimationNode *new_parent;
- //this is the slowest part of processing, but as strings process in powers of 2, and the paths always exist, it will not result in that many allocations
+ // This is the slowest part of processing, but as strings process in powers of 2, and the paths always exist, it will not result in that many allocations.
if (p_new_parent) {
new_parent = p_new_parent;
new_path = String(base_path) + String(p_subpath) + "/";
@@ -286,6 +286,9 @@ double AnimationNode::_blend_node(const StringName &p_subpath, const Vector<Stri
new_path = String(parent->base_path) + String(p_subpath) + "/";
}
+ // If tracks for blending don't exist for one of the animations, Rest or RESET animation is blended as init animation instead.
+ // Then, blend weight is 0 means that the init animation blend weight is 1.
+ // Therefore, the blending process must be executed even if the blend weight is 0.
if (!p_seek && p_optimize && !any_valid) {
return p_node->_pre_process(new_path, new_parent, state, 0, p_seek, p_seek_root, p_connections);
}
@@ -965,6 +968,10 @@ void AnimationTree::_process_graph(double p_delta) {
#endif // _3D_DISABLED
for (int i = 0; i < a->get_track_count(); i++) {
+ if (!a->track_is_enabled(i)) {
+ continue;
+ }
+
NodePath path = a->track_get_path(i);
ERR_CONTINUE(!track_cache.has(path));
@@ -1323,12 +1330,21 @@ void AnimationTree::_process_graph(double p_delta) {
if (blend < CMP_EPSILON) {
continue; //nothing to blend
}
- List<int> indices;
- a->value_track_get_key_indices(i, time, delta, &indices, pingponged);
- for (int &F : indices) {
- Variant value = a->track_get_key_value(i, F);
+ if (seeked) {
+ int idx = a->track_find_key(i, time);
+ if (idx < 0) {
+ continue;
+ }
+ Variant value = a->track_get_key_value(i, idx);
t->object->set_indexed(t->subpath, value);
+ } else {
+ List<int> indices;
+ a->value_track_get_key_indices(i, time, delta, &indices, pingponged);
+ for (int &F : indices) {
+ Variant value = a->track_get_key_value(i, F);
+ t->object->set_indexed(t->subpath, value);
+ }
}
}
@@ -1337,20 +1353,27 @@ void AnimationTree::_process_graph(double p_delta) {
if (blend < CMP_EPSILON) {
continue; //nothing to blend
}
- if (!seeked && Math::is_zero_approx(delta)) {
- continue;
- }
TrackCacheMethod *t = static_cast<TrackCacheMethod *>(track);
- List<int> indices;
-
- a->method_track_get_key_indices(i, time, delta, &indices, pingponged);
-
- for (int &F : indices) {
- StringName method = a->method_track_get_name(i, F);
- Vector<Variant> params = a->method_track_get_params(i, F);
+ if (seeked) {
+ int idx = a->track_find_key(i, time);
+ if (idx < 0) {
+ continue;
+ }
+ StringName method = a->method_track_get_name(i, idx);
+ Vector<Variant> params = a->method_track_get_params(i, idx);
if (can_call) {
- _call_object(t->object, method, params, true);
+ _call_object(t->object, method, params, false);
+ }
+ } else {
+ List<int> indices;
+ a->method_track_get_key_indices(i, time, delta, &indices, pingponged);
+ for (int &F : indices) {
+ StringName method = a->method_track_get_name(i, F);
+ Vector<Variant> params = a->method_track_get_params(i, F);
+ if (can_call) {
+ _call_object(t->object, method, params, true);
+ }
}
}
} break;
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 5ddc38a0b9..036d00345d 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -3286,7 +3286,7 @@ void Control::_bind_methods() {
ADD_GROUP("Layout", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_contents"), "set_clip_contents", "is_clipping_contents");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "minimum_size"), "set_custom_minimum_size", "get_custom_minimum_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "minimum_size", PROPERTY_HINT_NONE, "suffix:px"), "set_custom_minimum_size", "get_custom_minimum_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction");
ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_mode", PROPERTY_HINT_ENUM, "Position,Anchors,Container,Uncontrolled", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_layout_mode", "_get_layout_mode");
ADD_PROPERTY_DEFAULT("layout_mode", LayoutMode::LAYOUT_MODE_POSITION);
@@ -3321,7 +3321,7 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_NONE), "_set_global_position", "get_global_position");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "pivot_offset"), "set_pivot_offset", "get_pivot_offset");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "pivot_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_pivot_offset", "get_pivot_offset");
ADD_SUBGROUP("Container Sizing", "size_flags_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_h_size_flags", "get_h_size_flags");
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index b8c83ac89e..3e7b0a2808 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -188,7 +188,10 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
Vector<Vector3> faces;
faces.resize(faces_size);
+ Vector<int32_t> surface_indices;
+ surface_indices.resize(faces_size / 3);
Vector3 *facesw = faces.ptrw();
+ int32_t *surface_indicesw = surface_indices.ptrw();
int widx = 0;
@@ -210,6 +213,8 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
Vector<Vector3> vertices = a[ARRAY_VERTEX];
const Vector3 *vr = vertices.ptr();
+ int32_t from_index = widx / 3;
+
if (surface_get_format(i) & ARRAY_FORMAT_INDEX) {
int ic = surface_get_array_index_len(i);
Vector<int> indices = a[ARRAY_INDEX];
@@ -241,6 +246,12 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
}
}
}
+
+ int32_t to_index = widx / 3;
+
+ for (int j = from_index; j < to_index; j++) {
+ surface_indicesw[j] = i;
+ }
}
triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh));
diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp
index 9d586c6f03..dba53338eb 100644
--- a/scene/resources/resource_format_text.cpp
+++ b/scene/resources/resource_format_text.cpp
@@ -40,6 +40,8 @@
// Version 3: new string ID for ext/subresources, breaks forward compat.
#define FORMAT_VERSION 3
+#define BINARY_FORMAT_VERSION 4
+
#include "core/io/dir_access.h"
#include "core/version.h"
@@ -66,12 +68,8 @@ Error ResourceLoaderText::_parse_sub_resource_dummy(DummyReadData *p_data, Varia
String unique_id = token.value;
if (!p_data->resource_map.has(unique_id)) {
- Ref<DummyResource> dr;
- dr.instantiate();
- dr->set_scene_unique_id(unique_id);
- p_data->resource_map[unique_id] = dr;
- uint32_t im_size = p_data->resource_index_map.size();
- p_data->resource_index_map.insert(dr, im_size);
+ r_err_str = "Found unique_id reference before mapping, sub-resources stored out of order in resource file";
+ return ERR_PARSE_ERROR;
}
r_res = p_data->resource_map[unique_id];
@@ -1078,7 +1076,7 @@ Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_pa
wf->store_32(0); //64 bits file, false for now
wf->store_32(VERSION_MAJOR);
wf->store_32(VERSION_MINOR);
- static const int save_format_version = 3; //use format version 3 for saving
+ static const int save_format_version = BINARY_FORMAT_VERSION;
wf->store_32(save_format_version);
bs_save_unicode_string(wf, is_scene ? "PackedScene" : resource_type);
@@ -1176,7 +1174,7 @@ Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_pa
while (next_tag.name == "sub_resource" || next_tag.name == "resource") {
String type;
- int id = -1;
+ String id;
bool main_res;
if (next_tag.name == "sub_resource") {
@@ -1197,15 +1195,26 @@ Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_pa
type = next_tag.fields["type"];
id = next_tag.fields["id"];
main_res = false;
+
+ if (!dummy_read.resource_map.has(id)) {
+ Ref<DummyResource> dr;
+ dr.instantiate();
+ dr->set_scene_unique_id(id);
+ dummy_read.resource_map[id] = dr;
+ uint32_t im_size = dummy_read.resource_index_map.size();
+ dummy_read.resource_index_map.insert(dr, im_size);
+ }
+
} else {
type = res_type;
- id = 0; //used for last anyway
+ String uid_text = ResourceUID::get_singleton()->id_to_text(res_uid);
+ id = type + "_" + uid_text.replace("uid://", "").replace("<invalid>", "0");
main_res = true;
}
local_offsets.push_back(wf2->get_position());
- bs_save_unicode_string(wf, "local://" + itos(id));
+ bs_save_unicode_string(wf, "local://" + id);
local_pointers_pos.push_back(wf->get_position());
wf->store_64(0); //temp local offset
@@ -1274,7 +1283,8 @@ Error ResourceLoaderText::save_as_binary(Ref<FileAccess> p_f, const String &p_pa
List<PropertyInfo> props;
packed_scene->get_property_list(&props);
- bs_save_unicode_string(wf, "local://0");
+ String id = "PackedScene_" + ResourceUID::get_singleton()->id_to_text(res_uid).replace("uid://", "").replace("<invalid>", "0");
+ bs_save_unicode_string(wf, "local://" + id);
local_pointers_pos.push_back(wf->get_position());
wf->store_64(0); //temp local offset
@@ -1564,17 +1574,17 @@ String ResourceFormatSaverTextInstance::_write_resources(void *ud, const Ref<Res
String ResourceFormatSaverTextInstance::_write_resource(const Ref<Resource> &res) {
if (external_resources.has(res)) {
- return "ExtResource( \"" + external_resources[res] + "\" )";
+ return "ExtResource(\"" + external_resources[res] + "\")";
} else {
if (internal_resources.has(res)) {
- return "SubResource( \"" + internal_resources[res] + "\" )";
+ return "SubResource(\"" + internal_resources[res] + "\")";
} else if (!res->is_built_in()) {
if (res->get_path() == local_path) { //circular reference attempt
return "null";
}
//external resource
String path = relative_paths ? local_path.path_to_file(res->get_path()) : res->get_path();
- return "Resource( \"" + path + "\" )";
+ return "Resource(\"" + path + "\")";
} else {
ERR_FAIL_V_MSG("null", "Resource was not pre cached for the resource section, bug?");
//internal resource
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 8976aa17d2..13b671e562 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -5058,11 +5058,11 @@ Vector2i TileData::get_texture_offset() const {
return tex_offset;
}
-void TileData::set_material(Ref<ShaderMaterial> p_material) {
+void TileData::set_material(Ref<Material> p_material) {
material = p_material;
emit_signal(SNAME("changed"));
}
-Ref<ShaderMaterial> TileData::get_material() const {
+Ref<Material> TileData::get_material() const {
return material;
}
@@ -5700,7 +5700,7 @@ void TileData::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transpose"), "set_transpose", "get_transpose");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "texture_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_texture_offset", "get_texture_offset");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate");
- ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial"), "set_material", "get_material");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "CanvasItemMaterial,ShaderMaterial"), "set_material", "get_material");
ADD_PROPERTY(PropertyInfo(Variant::INT, "z_index"), "set_z_index", "get_z_index");
ADD_PROPERTY(PropertyInfo(Variant::INT, "y_sort_origin"), "set_y_sort_origin", "get_y_sort_origin");
diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h
index 181782e5af..7368d2bd87 100644
--- a/scene/resources/tile_set.h
+++ b/scene/resources/tile_set.h
@@ -164,7 +164,7 @@ private:
String name;
Ref<Texture2D> texture;
Vector2 tex_offset;
- Ref<ShaderMaterial> material;
+ Ref<Material> material;
Rect2 region;
int tile_mode = 0;
Color modulate = Color(1, 1, 1);
@@ -783,7 +783,7 @@ private:
bool flip_v = false;
bool transpose = false;
Vector2i tex_offset = Vector2i();
- Ref<ShaderMaterial> material = Ref<ShaderMaterial>();
+ Ref<Material> material = Ref<Material>();
Color modulate = Color(1.0, 1.0, 1.0, 1.0);
int z_index = 0;
int y_sort_origin = 0;
@@ -864,8 +864,8 @@ public:
void set_texture_offset(Vector2i p_texture_offset);
Vector2i get_texture_offset() const;
- void set_material(Ref<ShaderMaterial> p_material);
- Ref<ShaderMaterial> get_material() const;
+ void set_material(Ref<Material> p_material);
+ Ref<Material> get_material() const;
void set_modulate(Color p_modulate);
Color get_modulate() const;
void set_z_index(int p_z_index);
diff --git a/servers/SCsub b/servers/SCsub
index 66a1b9b26f..2ce90e970b 100644
--- a/servers/SCsub
+++ b/servers/SCsub
@@ -14,6 +14,7 @@ SConscript("audio/SCsub")
SConscript("text/SCsub")
SConscript("debugger/SCsub")
SConscript("extensions/SCsub")
+SConscript("movie_writer/SCsub")
lib = env.add_library("servers", env.servers_sources)
diff --git a/servers/audio/audio_driver_dummy.cpp b/servers/audio/audio_driver_dummy.cpp
index 635e4601c7..60eb657923 100644
--- a/servers/audio/audio_driver_dummy.cpp
+++ b/servers/audio/audio_driver_dummy.cpp
@@ -33,22 +33,24 @@
#include "core/config/project_settings.h"
#include "core/os/os.h"
+AudioDriverDummy *AudioDriverDummy::singleton = nullptr;
+
Error AudioDriverDummy::init() {
active = false;
thread_exited = false;
exit_thread = false;
samples_in = nullptr;
- mix_rate = GLOBAL_GET("audio/driver/mix_rate");
- speaker_mode = SPEAKER_MODE_STEREO;
- channels = 2;
-
- int latency = GLOBAL_GET("audio/driver/output_latency");
- buffer_frames = closest_power_of_2(latency * mix_rate / 1000);
+ if (mix_rate == -1) {
+ mix_rate = GLOBAL_GET("audio/driver/mix_rate");
+ }
+ channels = get_channels();
samples_in = memnew_arr(int32_t, (size_t)buffer_frames * channels);
- thread.start(AudioDriverDummy::thread_func, this);
+ if (use_threads) {
+ thread.start(AudioDriverDummy::thread_func, this);
+ }
return OK;
};
@@ -93,11 +95,56 @@ void AudioDriverDummy::unlock() {
mutex.unlock();
};
+void AudioDriverDummy::set_use_threads(bool p_use_threads) {
+ use_threads = p_use_threads;
+}
+
+void AudioDriverDummy::set_speaker_mode(SpeakerMode p_mode) {
+ speaker_mode = p_mode;
+}
+
+void AudioDriverDummy::set_mix_rate(int p_rate) {
+ mix_rate = p_rate;
+}
+
+uint32_t AudioDriverDummy::get_channels() const {
+ static const int channels_for_mode[4] = { 2, 4, 8, 16 };
+ return channels_for_mode[speaker_mode];
+}
+
+void AudioDriverDummy::mix_audio(int p_frames, int32_t *p_buffer) {
+ ERR_FAIL_COND(!active); // If not active, should not mix.
+ ERR_FAIL_COND(use_threads == true); // If using threads, this will not work well.
+
+ uint32_t todo = p_frames;
+ while (todo) {
+ uint32_t to_mix = MIN(buffer_frames, todo);
+ lock();
+ audio_server_process(to_mix, samples_in);
+ unlock();
+
+ uint32_t total_samples = to_mix * channels;
+
+ for (uint32_t i = 0; i < total_samples; i++) {
+ p_buffer[i] = samples_in[i];
+ }
+
+ todo -= to_mix;
+ p_buffer += total_samples;
+ }
+}
+
void AudioDriverDummy::finish() {
- exit_thread = true;
- thread.wait_to_finish();
+ if (use_threads) {
+ exit_thread = true;
+ thread.wait_to_finish();
+ }
if (samples_in) {
memdelete_arr(samples_in);
};
-};
+}
+
+AudioDriverDummy::AudioDriverDummy() {
+ singleton = this;
+}
diff --git a/servers/audio/audio_driver_dummy.h b/servers/audio/audio_driver_dummy.h
index 68b523e086..232a8d5e1f 100644
--- a/servers/audio/audio_driver_dummy.h
+++ b/servers/audio/audio_driver_dummy.h
@@ -44,9 +44,9 @@ class AudioDriverDummy : public AudioDriver {
static void thread_func(void *p_udata);
- unsigned int buffer_frames;
- unsigned int mix_rate;
- SpeakerMode speaker_mode;
+ uint32_t buffer_frames = 4096;
+ int32_t mix_rate = -1;
+ SpeakerMode speaker_mode = SPEAKER_MODE_STEREO;
int channels;
@@ -54,6 +54,10 @@ class AudioDriverDummy : public AudioDriver {
bool thread_exited;
mutable bool exit_thread;
+ bool use_threads = true;
+
+ static AudioDriverDummy *singleton;
+
public:
const char *get_name() const {
return "Dummy";
@@ -67,7 +71,17 @@ public:
virtual void unlock();
virtual void finish();
- AudioDriverDummy() {}
+ void set_use_threads(bool p_use_threads);
+ void set_speaker_mode(SpeakerMode p_mode);
+ void set_mix_rate(int p_rate);
+
+ uint32_t get_channels() const;
+
+ void mix_audio(int p_frames, int32_t *p_buffer);
+
+ static AudioDriverDummy *get_dummy_singleton() { return singleton; }
+
+ AudioDriverDummy();
~AudioDriverDummy() {}
};
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 3983bc5cb1..5d9a9e61dc 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -135,9 +135,10 @@ int AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale,
uint64_t mix_increment = uint64_t(((get_stream_sampling_rate() * p_rate_scale * playback_speed_scale) / double(target_rate)) * double(FP_LEN));
- int mixed_frames_total = p_frames;
+ int mixed_frames_total = -1;
- for (int i = 0; i < p_frames; i++) {
+ int i;
+ for (i = 0; i < p_frames; i++) {
uint32_t idx = CUBIC_INTERP_HISTORY + uint32_t(mix_offset >> FP_BITS);
//standard cubic interpolation (great quality/performance ratio)
//this used to be moved to a LUT for greater performance, but nowadays CPU speed is generally faster than memory.
@@ -147,7 +148,7 @@ int AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale,
AudioFrame y2 = internal_buffer[idx - 1];
AudioFrame y3 = internal_buffer[idx - 0];
- if (idx <= internal_buffer_end && idx >= internal_buffer_end && mixed_frames_total == p_frames) {
+ if (idx >= internal_buffer_end && mixed_frames_total == -1) {
// The internal buffer ends somewhere in this range, and we haven't yet recorded the number of good frames we have.
mixed_frames_total = i;
}
@@ -167,24 +168,20 @@ int AudioStreamPlaybackResampled::mix(AudioFrame *p_buffer, float p_rate_scale,
internal_buffer[1] = internal_buffer[INTERNAL_BUFFER_LEN + 1];
internal_buffer[2] = internal_buffer[INTERNAL_BUFFER_LEN + 2];
internal_buffer[3] = internal_buffer[INTERNAL_BUFFER_LEN + 3];
- if (is_playing()) {
- int mixed_frames = _mix_internal(internal_buffer + 4, INTERNAL_BUFFER_LEN);
- if (mixed_frames != INTERNAL_BUFFER_LEN) {
- // internal_buffer[mixed_frames] is the first frame of silence.
- internal_buffer_end = mixed_frames;
- } else {
- // The internal buffer does not contain the first frame of silence.
- internal_buffer_end = -1;
- }
+ int mixed_frames = _mix_internal(internal_buffer + 4, INTERNAL_BUFFER_LEN);
+ if (mixed_frames != INTERNAL_BUFFER_LEN) {
+ // internal_buffer[mixed_frames] is the first frame of silence.
+ internal_buffer_end = mixed_frames;
} else {
- //fill with silence, not playing
- for (int j = 0; j < INTERNAL_BUFFER_LEN; ++j) {
- internal_buffer[j + 4] = AudioFrame(0, 0);
- }
+ // The internal buffer does not contain the first frame of silence.
+ internal_buffer_end = -1;
}
mix_offset -= (INTERNAL_BUFFER_LEN << FP_BITS);
}
}
+ if (mixed_frames_total == -1 && i == p_frames) {
+ mixed_frames_total = p_frames;
+ }
return mixed_frames_total;
}
diff --git a/servers/movie_writer/SCsub b/servers/movie_writer/SCsub
new file mode 100644
index 0000000000..86681f9c74
--- /dev/null
+++ b/servers/movie_writer/SCsub
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env.add_source_files(env.servers_sources, "*.cpp")
diff --git a/servers/movie_writer/movie_writer.cpp b/servers/movie_writer/movie_writer.cpp
new file mode 100644
index 0000000000..ac60dc3b9a
--- /dev/null
+++ b/servers/movie_writer/movie_writer.cpp
@@ -0,0 +1,306 @@
+/*************************************************************************/
+/* movie_writer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "movie_writer.h"
+#include "core/config/project_settings.h"
+#include "core/io/dir_access.h"
+
+MovieWriter *MovieWriter::writers[MovieWriter::MAX_WRITERS];
+uint32_t MovieWriter::writer_count = 0;
+
+void MovieWriter::add_writer(MovieWriter *p_writer) {
+ ERR_FAIL_COND(writer_count == MAX_WRITERS);
+ writers[writer_count++] = p_writer;
+}
+
+MovieWriter *MovieWriter::find_writer_for_file(const String &p_file) {
+ for (int32_t i = writer_count - 1; i >= 0; i--) { // More recent last, to have override ability.
+ if (writers[i]->handles_file(p_file)) {
+ return writers[i];
+ }
+ }
+ return nullptr;
+}
+
+uint32_t MovieWriter::get_audio_mix_rate() const {
+ uint32_t ret = 0;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_audio_mix_rate, ret)) {
+ return ret;
+ }
+ return 48000;
+}
+AudioServer::SpeakerMode MovieWriter::get_audio_speaker_mode() const {
+ AudioServer::SpeakerMode ret = AudioServer::SPEAKER_MODE_STEREO;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_audio_speaker_mode, ret)) {
+ return ret;
+ }
+ return AudioServer::SPEAKER_MODE_STEREO;
+}
+
+Error MovieWriter::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
+ Error ret = OK;
+ if (GDVIRTUAL_REQUIRED_CALL(_write_begin, p_movie_size, p_fps, p_base_path, ret)) {
+ return ret;
+ }
+ return ERR_UNCONFIGURED;
+}
+
+Error MovieWriter::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) {
+ Error ret = OK;
+ if (GDVIRTUAL_REQUIRED_CALL(_write_frame, p_image, p_audio_data, ret)) {
+ return ret;
+ }
+ return ERR_UNCONFIGURED;
+}
+
+void MovieWriter::write_end() {
+ GDVIRTUAL_REQUIRED_CALL(_write_end);
+}
+
+bool MovieWriter::handles_file(const String &p_path) const {
+ bool ret = false;
+ if (GDVIRTUAL_REQUIRED_CALL(_handles_file, p_path, ret)) {
+ return ret;
+ }
+ return false;
+}
+
+void MovieWriter::get_supported_extensions(List<String> *r_extensions) const {
+ Vector<String> exts;
+ if (GDVIRTUAL_REQUIRED_CALL(_get_supported_extensions, exts)) {
+ for (int i = 0; i < exts.size(); i++) {
+ r_extensions->push_back(exts[i]);
+ }
+ }
+}
+
+void MovieWriter::begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
+ mix_rate = get_audio_mix_rate();
+ AudioDriverDummy::get_dummy_singleton()->set_mix_rate(mix_rate);
+ AudioDriverDummy::get_dummy_singleton()->set_speaker_mode(AudioDriver::SpeakerMode(get_audio_speaker_mode()));
+ fps = p_fps;
+ if ((mix_rate % fps) != 0) {
+ WARN_PRINT("Audio mix rate (" + itos(mix_rate) + ") can not be divided by fps (" + itos(fps) + "). Audio may go out of sync over time.");
+ }
+
+ audio_channels = AudioDriverDummy::get_dummy_singleton()->get_channels();
+ audio_mix_buffer.resize(mix_rate * audio_channels / fps);
+
+ write_begin(p_movie_size, p_fps, p_base_path);
+}
+
+void MovieWriter::_bind_methods() {
+ ClassDB::bind_static_method("MovieWriter", D_METHOD("add_writer", "writer"), &MovieWriter::add_writer);
+
+ GDVIRTUAL_BIND(_get_audio_mix_rate)
+ GDVIRTUAL_BIND(_get_audio_speaker_mode)
+
+ GDVIRTUAL_BIND(_handles_file, "path")
+
+ GDVIRTUAL_BIND(_write_begin, "movie_size", "fps", "base_path")
+ GDVIRTUAL_BIND(_write_frame, "frame_image", "audio_frame_block")
+ GDVIRTUAL_BIND(_write_end)
+
+ GLOBAL_DEF("editor/movie_writer/mix_rate_hz", 48000);
+ GLOBAL_DEF("editor/movie_writer/speaker_mode", 0);
+ ProjectSettings::get_singleton()->set_custom_property_info("editor/movie_writer/speaker_mode", PropertyInfo(Variant::INT, "editor/movie_writer/speaker_mode", PROPERTY_HINT_ENUM, "Stereo,3.1,5.1,7.1"));
+ GLOBAL_DEF("editor/movie_writer/mjpeg_quality", 0.75);
+ // used by the editor
+ GLOBAL_DEF_BASIC("editor/movie_writer/movie_file", "");
+ GLOBAL_DEF_BASIC("editor/movie_writer/disable_vsync", false);
+ GLOBAL_DEF_BASIC("editor/movie_writer/fps", 60);
+ ProjectSettings::get_singleton()->set_custom_property_info("editor/movie_writer/fps", PropertyInfo(Variant::INT, "editor/movie_writer/fps", PROPERTY_HINT_RANGE, "1,300,1"));
+}
+
+void MovieWriter::set_extensions_hint() {
+ RBSet<String> found;
+ for (uint32_t i = 0; i < writer_count; i++) {
+ List<String> extensions;
+ writers[i]->get_supported_extensions(&extensions);
+ for (const String &ext : extensions) {
+ found.insert(ext);
+ }
+ }
+
+ String ext_hint;
+
+ for (const String &S : found) {
+ if (ext_hint != "") {
+ ext_hint += ",";
+ }
+ ext_hint += "*." + S;
+ }
+ ProjectSettings::get_singleton()->set_custom_property_info("editor/movie_writer/movie_file", PropertyInfo(Variant::STRING, "editor/movie_writer/movie_file", PROPERTY_HINT_GLOBAL_SAVE_FILE, ext_hint));
+}
+
+void MovieWriter::add_frame(const Ref<Image> &p_image) {
+ AudioDriverDummy::get_dummy_singleton()->mix_audio(mix_rate / fps, audio_mix_buffer.ptr());
+ write_frame(p_image, audio_mix_buffer.ptr());
+}
+
+void MovieWriter::end() {
+ write_end();
+}
+/////////////////////////////////////////
+
+uint32_t MovieWriterPNGWAV::get_audio_mix_rate() const {
+ return mix_rate;
+}
+AudioServer::SpeakerMode MovieWriterPNGWAV::get_audio_speaker_mode() const {
+ return speaker_mode;
+}
+
+void MovieWriterPNGWAV::get_supported_extensions(List<String> *r_extensions) const {
+ r_extensions->push_back("png");
+}
+
+bool MovieWriterPNGWAV::handles_file(const String &p_path) const {
+ return p_path.get_extension().to_lower() == "png";
+}
+
+String MovieWriterPNGWAV::zeros_str(uint32_t p_index) {
+ char zeros[MAX_TRAILING_ZEROS + 1];
+ for (uint32_t i = 0; i < MAX_TRAILING_ZEROS; i++) {
+ uint32_t idx = MAX_TRAILING_ZEROS - i - 1;
+ uint32_t digit = (p_index / uint32_t(Math::pow(double(10), double(idx)))) % 10;
+ zeros[i] = '0' + digit;
+ }
+ zeros[MAX_TRAILING_ZEROS] = 0;
+ return zeros;
+}
+
+Error MovieWriterPNGWAV::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
+ // Quick & Dirty PNGWAV Code based on - https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference
+
+ base_path = p_base_path.get_basename();
+ if (base_path.is_relative_path()) {
+ base_path = "res://" + base_path;
+ }
+
+ {
+ //Remove existing files before writing anew
+ uint32_t idx = 0;
+ Ref<DirAccess> d = DirAccess::open(base_path.get_base_dir());
+ String file = base_path.get_file();
+ while (true) {
+ String path = file + zeros_str(idx) + ".png";
+ if (d->remove(path) != OK) {
+ break;
+ }
+ }
+ }
+
+ f_wav = FileAccess::open(base_path + ".wav", FileAccess::WRITE_READ);
+ ERR_FAIL_COND_V(f_wav.is_null(), ERR_CANT_OPEN);
+
+ fps = p_fps;
+
+ f_wav->store_buffer((const uint8_t *)"RIFF", 4);
+ int total_size = 4 /* WAVE */ + 8 /* fmt+size */ + 16 /* format */ + 8 /* data+size */;
+ f_wav->store_32(total_size); //will store final later
+ f_wav->store_buffer((const uint8_t *)"WAVE", 4);
+
+ /* FORMAT CHUNK */
+
+ f_wav->store_buffer((const uint8_t *)"fmt ", 4);
+
+ uint32_t channels = 2;
+ switch (speaker_mode) {
+ case AudioServer::SPEAKER_MODE_STEREO:
+ channels = 2;
+ break;
+ case AudioServer::SPEAKER_SURROUND_31:
+ channels = 4;
+ break;
+ case AudioServer::SPEAKER_SURROUND_51:
+ channels = 6;
+ break;
+ case AudioServer::SPEAKER_SURROUND_71:
+ channels = 8;
+ break;
+ }
+
+ f_wav->store_32(16); //standard format, no extra fields
+ f_wav->store_16(1); // compression code, standard PCM
+ f_wav->store_16(channels); //CHANNELS: 2
+
+ f_wav->store_32(mix_rate);
+
+ /* useless stuff the format asks for */
+
+ int bits_per_sample = 32;
+ int blockalign = bits_per_sample / 8 * channels;
+ int bytes_per_sec = mix_rate * blockalign;
+
+ audio_block_size = (mix_rate / fps) * blockalign;
+
+ f_wav->store_32(bytes_per_sec);
+ f_wav->store_16(blockalign); // block align (unused)
+ f_wav->store_16(bits_per_sample);
+
+ /* DATA CHUNK */
+
+ f_wav->store_buffer((const uint8_t *)"data", 4);
+
+ f_wav->store_32(0); //data size... wooh
+ wav_data_size_pos = f_wav->get_position();
+
+ return OK;
+}
+
+Error MovieWriterPNGWAV::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) {
+ ERR_FAIL_COND_V(!f_wav.is_valid(), ERR_UNCONFIGURED);
+
+ Vector<uint8_t> png_buffer = p_image->save_png_to_buffer();
+
+ Ref<FileAccess> fi = FileAccess::open(base_path + zeros_str(frame_count) + ".png", FileAccess::WRITE);
+ fi->store_buffer(png_buffer.ptr(), png_buffer.size());
+ f_wav->store_buffer((const uint8_t *)p_audio_data, audio_block_size);
+
+ frame_count++;
+
+ return OK;
+}
+
+void MovieWriterPNGWAV::write_end() {
+ if (f_wav.is_valid()) {
+ uint32_t total_size = 4 /* WAVE */ + 8 /* fmt+size */ + 16 /* format */ + 8 /* data+size */;
+ uint32_t datasize = f_wav->get_position() - wav_data_size_pos;
+ f_wav->seek(4);
+ f_wav->store_32(total_size + datasize);
+ f_wav->seek(0x28);
+ f_wav->store_32(datasize);
+ }
+}
+
+MovieWriterPNGWAV::MovieWriterPNGWAV() {
+ mix_rate = GLOBAL_GET("editor/movie_writer/mix_rate_hz");
+ speaker_mode = AudioServer::SpeakerMode(int(GLOBAL_GET("editor/movie_writer/speaker_mode")));
+}
diff --git a/servers/movie_writer/movie_writer.h b/servers/movie_writer/movie_writer.h
new file mode 100644
index 0000000000..11e739df39
--- /dev/null
+++ b/servers/movie_writer/movie_writer.h
@@ -0,0 +1,123 @@
+/*************************************************************************/
+/* movie_writer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MOVIE_WRITER_H
+#define MOVIE_WRITER_H
+
+#include "core/templates/local_vector.h"
+#include "servers/audio/audio_driver_dummy.h"
+#include "servers/audio_server.h"
+
+class MovieWriter : public Object {
+ GDCLASS(MovieWriter, Object);
+
+ uint64_t fps = 0;
+ uint64_t mix_rate = 0;
+ uint32_t audio_channels = 0;
+
+ LocalVector<int32_t> audio_mix_buffer;
+
+ enum {
+ MAX_WRITERS = 8
+ };
+ static MovieWriter *writers[];
+ static uint32_t writer_count;
+
+protected:
+ virtual uint32_t get_audio_mix_rate() const;
+ virtual AudioServer::SpeakerMode get_audio_speaker_mode() const;
+
+ virtual Error write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path);
+ virtual Error write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data);
+ virtual void write_end();
+
+ GDVIRTUAL0RC(uint32_t, _get_audio_mix_rate)
+ GDVIRTUAL0RC(AudioServer::SpeakerMode, _get_audio_speaker_mode)
+
+ GDVIRTUAL1RC(bool, _handles_file, const String &)
+ GDVIRTUAL0RC(Vector<String>, _get_supported_extensions)
+
+ GDVIRTUAL3R(Error, _write_begin, const Size2i &, uint32_t, const String &)
+ GDVIRTUAL2R(Error, _write_frame, const Ref<Image> &, GDNativeConstPtr<int32_t>)
+ GDVIRTUAL0(_write_end)
+
+ static void _bind_methods();
+
+public:
+ virtual bool handles_file(const String &p_path) const;
+ virtual void get_supported_extensions(List<String> *r_extensions) const;
+
+ static void add_writer(MovieWriter *p_writer);
+ static MovieWriter *find_writer_for_file(const String &p_file);
+
+ void begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path);
+ void add_frame(const Ref<Image> &p_image);
+
+ static void set_extensions_hint();
+
+ void end();
+};
+
+class MovieWriterPNGWAV : public MovieWriter {
+ GDCLASS(MovieWriterPNGWAV, MovieWriter)
+
+ enum {
+ MAX_TRAILING_ZEROS = 8 // more than 10 days at 60fps, no hard drive can put up with this anyway :)
+ };
+
+ uint32_t mix_rate = 48000;
+ AudioServer::SpeakerMode speaker_mode = AudioServer::SPEAKER_MODE_STEREO;
+ String base_path;
+ uint32_t frame_count = 0;
+ uint32_t fps = 0;
+
+ uint32_t audio_block_size = 0;
+
+ Ref<FileAccess> f_wav;
+ uint32_t wav_data_size_pos = 0;
+
+ String zeros_str(uint32_t p_index);
+
+protected:
+ virtual uint32_t get_audio_mix_rate() const override;
+ virtual AudioServer::SpeakerMode get_audio_speaker_mode() const override;
+ virtual void get_supported_extensions(List<String> *r_extensions) const override;
+
+ virtual Error write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) override;
+ virtual Error write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) override;
+ virtual void write_end() override;
+
+ virtual bool handles_file(const String &p_path) const override;
+
+public:
+ MovieWriterPNGWAV();
+};
+
+#endif // VIDEO_WRITER_H
diff --git a/servers/movie_writer/movie_writer_mjpeg.cpp b/servers/movie_writer/movie_writer_mjpeg.cpp
new file mode 100644
index 0000000000..b0c65e768d
--- /dev/null
+++ b/servers/movie_writer/movie_writer_mjpeg.cpp
@@ -0,0 +1,263 @@
+/*************************************************************************/
+/* movie_writer_mjpeg.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "movie_writer_mjpeg.h"
+#include "core/config/project_settings.h"
+
+uint32_t MovieWriterMJPEG::get_audio_mix_rate() const {
+ return mix_rate;
+}
+AudioServer::SpeakerMode MovieWriterMJPEG::get_audio_speaker_mode() const {
+ return speaker_mode;
+}
+
+bool MovieWriterMJPEG::handles_file(const String &p_path) const {
+ return p_path.get_extension().to_lower() == "avi";
+}
+
+void MovieWriterMJPEG::get_supported_extensions(List<String> *r_extensions) const {
+ r_extensions->push_back("avi");
+}
+
+Error MovieWriterMJPEG::write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) {
+ // Quick & Dirty MJPEG Code based on - https://docs.microsoft.com/en-us/windows/win32/directshow/avi-riff-file-reference
+
+ base_path = p_base_path.get_basename();
+ if (base_path.is_relative_path()) {
+ base_path = "res://" + base_path;
+ }
+
+ base_path += ".avi";
+
+ f = FileAccess::open(base_path, FileAccess::WRITE_READ);
+
+ fps = p_fps;
+
+ ERR_FAIL_COND_V(f.is_null(), ERR_CANT_OPEN);
+
+ f->store_buffer((const uint8_t *)"RIFF", 4);
+ f->store_32(0); // Total length (update later)
+ f->store_buffer((const uint8_t *)"AVI ", 4);
+ f->store_buffer((const uint8_t *)"LIST", 4);
+ f->store_32(300); // 4 + 4 + 4 + 56 + 4 + 4 + 132 + 4 + 4 + 84
+ f->store_buffer((const uint8_t *)"hdrl", 4);
+ f->store_buffer((const uint8_t *)"avih", 4);
+ f->store_32(56);
+
+ f->store_32(1000000 / p_fps); // Microsecs per frame.
+ f->store_32(7000); // Max bytes per second
+ f->store_32(0); // Padding Granularity
+ f->store_32(16);
+ total_frames_ofs = f->get_position();
+ f->store_32(0); // Total frames (update later)
+ f->store_32(0); // Initial frames
+ f->store_32(1); // Streams
+ f->store_32(0); // Suggested buffer size
+ f->store_32(p_movie_size.width); // Movie Width
+ f->store_32(p_movie_size.height); // Movie Height
+ for (uint32_t i = 0; i < 4; i++) {
+ f->store_32(0); // Reserved.
+ }
+ f->store_buffer((const uint8_t *)"LIST", 4);
+ f->store_32(132); // 4 + 4 + 4 + 48 + 4 + 4 + 40 + 4 + 4 + 16
+ f->store_buffer((const uint8_t *)"strl", 4);
+ f->store_buffer((const uint8_t *)"strh", 4);
+ f->store_32(48);
+ f->store_buffer((const uint8_t *)"vids", 4);
+ f->store_buffer((const uint8_t *)"MJPG", 4);
+ f->store_32(0); // Flags
+ f->store_16(0); // Priority
+ f->store_16(0); // Language
+ f->store_32(0); // Initial Frames
+ f->store_32(1); // Scale
+ f->store_32(p_fps); // FPS
+ f->store_32(0); // Start
+ total_frames_ofs2 = f->get_position();
+ f->store_32(0); // Number of frames (to be updated later)
+ f->store_32(0); // Suggested Buffer Size
+ f->store_32(0); // Quality
+ f->store_32(0); // Sample Size
+
+ f->store_buffer((const uint8_t *)"strf", 4);
+ f->store_32(40); // Size.
+ f->store_32(40); // Size.
+
+ f->store_32(p_movie_size.width); // Width
+ f->store_32(p_movie_size.height); // Width
+ f->store_16(1); // Planes
+ f->store_16(24); // Bitcount
+ f->store_buffer((const uint8_t *)"MJPG", 4); // Compression
+
+ f->store_32(((p_movie_size.width * 24 / 8 + 3) & 0xFFFFFFFC) * p_movie_size.height); // SizeImage
+ f->store_32(0); // XPelsXMeter
+ f->store_32(0); // YPelsXMeter
+ f->store_32(0); // ClrUsed
+ f->store_32(0); // ClrImportant
+
+ f->store_buffer((const uint8_t *)"LIST", 4);
+ f->store_32(16);
+
+ f->store_buffer((const uint8_t *)"odml", 4);
+ f->store_buffer((const uint8_t *)"dmlh", 4);
+ f->store_32(4); // sizes
+
+ total_frames_ofs3 = f->get_position();
+ f->store_32(0); // Number of frames (to be updated later)
+
+ // Audio //
+
+ const uint32_t bit_depth = 32;
+ uint32_t channels = 2;
+ switch (speaker_mode) {
+ case AudioServer::SPEAKER_MODE_STEREO:
+ channels = 2;
+ break;
+ case AudioServer::SPEAKER_SURROUND_31:
+ channels = 4;
+ break;
+ case AudioServer::SPEAKER_SURROUND_51:
+ channels = 6;
+ break;
+ case AudioServer::SPEAKER_SURROUND_71:
+ channels = 8;
+ break;
+ }
+ uint32_t blockalign = bit_depth / 8 * channels;
+
+ f->store_buffer((const uint8_t *)"LIST", 4);
+ f->store_32(84); // 4 + 4 + 4 + 48 + 4 + 4 + 16
+ f->store_buffer((const uint8_t *)"strl", 4);
+ f->store_buffer((const uint8_t *)"strh", 4);
+ f->store_32(48);
+ f->store_buffer((const uint8_t *)"auds", 4);
+ f->store_32(0); // Handler
+ f->store_32(0); // Flags
+ f->store_16(0); // Priority
+ f->store_16(0); // Language
+ f->store_32(0); // Initial Frames
+ f->store_32(blockalign); // Scale
+ f->store_32(mix_rate * blockalign); // mix rate
+ f->store_32(0); // Start
+ total_audio_frames_ofs4 = f->get_position();
+ f->store_32(0); // Number of frames (to be updated later)
+ f->store_32(12288); // Suggested Buffer Size
+ f->store_32(0xFFFFFFFF); // Quality
+ f->store_32(blockalign); // Block Align to 32 bits
+
+ audio_block_size = (mix_rate / fps) * blockalign;
+
+ f->store_buffer((const uint8_t *)"strf", 4);
+ f->store_32(16); // Standard format, no extra fields
+ f->store_16(1); // Compression code, standard PCM
+ f->store_16(channels);
+ f->store_32(mix_rate); // Samples (frames) / Sec
+ f->store_32(mix_rate * blockalign); // Bytes / sec
+ f->store_16(blockalign); // Bytes / sec
+ f->store_16(bit_depth); // Bytes / sec
+
+ f->store_buffer((const uint8_t *)"LIST", 4);
+ movi_data_ofs = f->get_position();
+ f->store_32(0); // Number of frames (to be updated later)
+ f->store_buffer((const uint8_t *)"movi", 4);
+
+ return OK;
+}
+
+Error MovieWriterMJPEG::write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) {
+ ERR_FAIL_COND_V(!f.is_valid(), ERR_UNCONFIGURED);
+
+ Vector<uint8_t> jpg_buffer = p_image->save_jpg_to_buffer(quality);
+ uint32_t s = jpg_buffer.size();
+
+ f->store_buffer((const uint8_t *)"00db", 4); // Stream 0, Video
+ f->store_32(jpg_buffer.size()); // sizes
+ f->store_buffer(jpg_buffer.ptr(), jpg_buffer.size());
+ if (jpg_buffer.size() & 1) {
+ f->store_8(0);
+ s++;
+ }
+ jpg_frame_sizes.push_back(s);
+
+ f->store_buffer((const uint8_t *)"01wb", 4); // Stream 1, Audio.
+ f->store_32(audio_block_size);
+ f->store_buffer((const uint8_t *)p_audio_data, audio_block_size);
+
+ frame_count++;
+
+ return OK;
+}
+
+void MovieWriterMJPEG::write_end() {
+ if (f.is_valid()) {
+ // Finalize the file (frame indices)
+ f->store_buffer((const uint8_t *)"idx1", 4);
+ f->store_32(8 * 4 * frame_count);
+ uint32_t ofs = 4;
+ uint32_t all_data_size = 0;
+ for (uint32_t i = 0; i < frame_count; i++) {
+ f->store_buffer((const uint8_t *)"00db", 4);
+ f->store_32(16); // AVI_KEYFRAME
+ f->store_32(ofs);
+ f->store_32(jpg_frame_sizes[i]);
+
+ ofs += jpg_frame_sizes[i] + 8;
+
+ f->store_buffer((const uint8_t *)"01wb", 4);
+ f->store_32(16); // AVI_KEYFRAME
+ f->store_32(ofs);
+ f->store_32(audio_block_size);
+
+ ofs += audio_block_size + 8;
+ all_data_size += jpg_frame_sizes[i] + audio_block_size;
+ }
+
+ uint32_t file_size = f->get_position();
+ f->seek(4);
+ f->store_32(file_size - 78);
+ f->seek(total_frames_ofs);
+ f->store_32(frame_count);
+ f->seek(total_frames_ofs2);
+ f->store_32(frame_count);
+ f->seek(total_frames_ofs3);
+ f->store_32(frame_count);
+ f->seek(total_audio_frames_ofs4);
+ f->store_32(frame_count * mix_rate / fps);
+ f->seek(movi_data_ofs);
+ f->store_32(all_data_size + 4 + 16 * frame_count);
+
+ f.unref();
+ }
+}
+
+MovieWriterMJPEG::MovieWriterMJPEG() {
+ mix_rate = GLOBAL_GET("editor/movie_writer/mix_rate_hz");
+ speaker_mode = AudioServer::SpeakerMode(int(GLOBAL_GET("editor/movie_writer/speaker_mode")));
+ quality = GLOBAL_GET("editor/movie_writer/mjpeg_quality");
+}
diff --git a/servers/movie_writer/movie_writer_mjpeg.h b/servers/movie_writer/movie_writer_mjpeg.h
new file mode 100644
index 0000000000..822bedfedf
--- /dev/null
+++ b/servers/movie_writer/movie_writer_mjpeg.h
@@ -0,0 +1,73 @@
+/*************************************************************************/
+/* movie_writer_mjpeg.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef MOVIE_WRITER_MJPEG_H
+#define MOVIE_WRITER_MJPEG_H
+
+#include "servers/movie_writer/movie_writer.h"
+
+class MovieWriterMJPEG : public MovieWriter {
+ GDCLASS(MovieWriterMJPEG, MovieWriter)
+
+ uint32_t mix_rate = 48000;
+ AudioServer::SpeakerMode speaker_mode = AudioServer::SPEAKER_MODE_STEREO;
+ String base_path;
+ uint32_t frame_count = 0;
+ uint32_t fps = 0;
+ float quality = 0.75;
+
+ uint32_t audio_block_size = 0;
+
+ Vector<uint32_t> jpg_frame_sizes;
+
+ uint64_t total_frames_ofs = 0;
+ uint64_t total_frames_ofs2 = 0;
+ uint64_t total_frames_ofs3 = 0;
+ uint64_t total_audio_frames_ofs4 = 0;
+ uint64_t movi_data_ofs = 0;
+
+ Ref<FileAccess> f;
+
+protected:
+ virtual uint32_t get_audio_mix_rate() const override;
+ virtual AudioServer::SpeakerMode get_audio_speaker_mode() const override;
+ virtual void get_supported_extensions(List<String> *r_extensions) const override;
+
+ virtual Error write_begin(const Size2i &p_movie_size, uint32_t p_fps, const String &p_base_path) override;
+ virtual Error write_frame(const Ref<Image> &p_image, const int32_t *p_audio_data) override;
+ virtual void write_end() override;
+
+ virtual bool handles_file(const String &p_path) const override;
+
+public:
+ MovieWriterMJPEG();
+};
+
+#endif // MOVIE_WRITER_AVIJPEG_H
diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp
index 8bbe987cb7..d7d2340119 100644
--- a/servers/register_server_types.cpp
+++ b/servers/register_server_types.cpp
@@ -57,6 +57,8 @@
#include "camera_server.h"
#include "debugger/servers_debugger.h"
#include "display_server.h"
+#include "movie_writer/movie_writer.h"
+#include "movie_writer/movie_writer_mjpeg.h"
#include "navigation_server_2d.h"
#include "navigation_server_3d.h"
#include "physics_2d/godot_physics_server_2d.h"
@@ -107,6 +109,9 @@ static bool has_server_feature_callback(const String &p_feature) {
return false;
}
+static MovieWriterMJPEG *writer_mjpeg = nullptr;
+static MovieWriterPNGWAV *writer_pngwav = nullptr;
+
void register_server_types() {
shader_types = memnew(ShaderTypes);
@@ -239,6 +244,8 @@ void register_server_types() {
GDREGISTER_CLASS(PhysicsTestMotionParameters3D);
GDREGISTER_CLASS(PhysicsTestMotionResult3D);
+ GDREGISTER_VIRTUAL_CLASS(MovieWriter);
+
ServersDebugger::initialize();
// Physics 2D
@@ -254,11 +261,19 @@ void register_server_types() {
PhysicsServer3DManager::register_server("GodotPhysics3D", &_createGodotPhysics3DCallback);
PhysicsServer3DManager::set_default_server("GodotPhysics3D");
+
+ writer_mjpeg = memnew(MovieWriterMJPEG);
+ MovieWriter::add_writer(writer_mjpeg);
+
+ writer_pngwav = memnew(MovieWriterPNGWAV);
+ MovieWriter::add_writer(writer_pngwav);
}
void unregister_server_types() {
ServersDebugger::deinitialize();
memdelete(shader_types);
+ memdelete(writer_mjpeg);
+ memdelete(writer_pngwav);
}
void register_server_singletons() {
diff --git a/servers/rendering/dummy/environment/gi.h b/servers/rendering/dummy/environment/gi.h
new file mode 100644
index 0000000000..374f0c8923
--- /dev/null
+++ b/servers/rendering/dummy/environment/gi.h
@@ -0,0 +1,85 @@
+/*************************************************************************/
+/* gi.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef GI_DUMMY_H
+#define GI_DUMMY_H
+
+#include "servers/rendering/environment/renderer_gi.h"
+
+namespace RendererDummy {
+
+class GI : public RendererGI {
+public:
+ /* VOXEL GI API */
+
+ virtual RID voxel_gi_allocate() override { return RID(); }
+ virtual void voxel_gi_free(RID p_rid) override {}
+ virtual void voxel_gi_initialize(RID p_rid) override {}
+ virtual void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) override {}
+
+ virtual AABB voxel_gi_get_bounds(RID p_voxel_gi) const override { return AABB(); }
+ virtual Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const override { return Vector3i(); }
+ virtual Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const override { return Vector<uint8_t>(); }
+ virtual Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const override { return Vector<uint8_t>(); }
+ virtual Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const override { return Vector<uint8_t>(); }
+
+ virtual Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const override { return Vector<int>(); }
+ virtual Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const override { return Transform3D(); }
+
+ virtual void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) override {}
+ virtual float voxel_gi_get_dynamic_range(RID p_voxel_gi) const override { return 0; }
+
+ virtual void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) override {}
+ virtual float voxel_gi_get_propagation(RID p_voxel_gi) const override { return 0; }
+
+ virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_range) override {}
+ virtual float voxel_gi_get_energy(RID p_voxel_gi) const override { return 0.0; }
+
+ virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_range) override {}
+ virtual float voxel_gi_get_bias(RID p_voxel_gi) const override { return 0.0; }
+
+ virtual void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) override {}
+ virtual float voxel_gi_get_normal_bias(RID p_voxel_gi) const override { return 0.0; }
+
+ virtual void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) override {}
+ virtual bool voxel_gi_is_interior(RID p_voxel_gi) const override { return false; }
+
+ virtual void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) override {}
+ virtual bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const override { return false; }
+
+ virtual void voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) override {}
+ virtual float voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const override { return 0; }
+
+ virtual uint32_t voxel_gi_get_version(RID p_voxel_gi) const override { return 0; }
+};
+
+} // namespace RendererDummy
+
+#endif // !GI_DUMMY_H
diff --git a/servers/rendering/dummy/rasterizer_dummy.h b/servers/rendering/dummy/rasterizer_dummy.h
index 5c6fcc8386..9c2bd45cce 100644
--- a/servers/rendering/dummy/rasterizer_dummy.h
+++ b/servers/rendering/dummy/rasterizer_dummy.h
@@ -34,6 +34,7 @@
#include "core/templates/rid_owner.h"
#include "core/templates/self_list.h"
#include "scene/resources/mesh.h"
+#include "servers/rendering/dummy/environment/gi.h"
#include "servers/rendering/dummy/rasterizer_canvas_dummy.h"
#include "servers/rendering/dummy/rasterizer_scene_dummy.h"
#include "servers/rendering/dummy/rasterizer_storage_dummy.h"
@@ -57,6 +58,7 @@ protected:
RendererDummy::MeshStorage mesh_storage;
RendererDummy::ParticlesStorage particles_storage;
RendererDummy::TextureStorage texture_storage;
+ RendererDummy::GI gi;
RasterizerStorageDummy storage;
RasterizerSceneDummy scene;
@@ -66,6 +68,7 @@ public:
RendererMeshStorage *get_mesh_storage() override { return &mesh_storage; };
RendererParticlesStorage *get_particles_storage() override { return &particles_storage; };
RendererTextureStorage *get_texture_storage() override { return &texture_storage; };
+ RendererGI *get_gi() override { return &gi; };
RendererStorage *get_storage() override { return &storage; }
RendererCanvasRender *get_canvas() override { return &canvas; }
RendererSceneRender *get_scene() override { return &scene; }
diff --git a/servers/rendering/dummy/rasterizer_storage_dummy.h b/servers/rendering/dummy/rasterizer_storage_dummy.h
index 596960786a..7f637d2c42 100644
--- a/servers/rendering/dummy/rasterizer_storage_dummy.h
+++ b/servers/rendering/dummy/rasterizer_storage_dummy.h
@@ -38,47 +38,6 @@ class RasterizerStorageDummy : public RendererStorage {
public:
void base_update_dependency(RID p_base, DependencyTracker *p_instance) override {}
- /* VOXEL GI API */
-
- RID voxel_gi_allocate() override { return RID(); }
- void voxel_gi_initialize(RID p_rid) override {}
- void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) override {}
-
- AABB voxel_gi_get_bounds(RID p_voxel_gi) const override { return AABB(); }
- Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const override { return Vector3i(); }
- Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const override { return Vector<uint8_t>(); }
- Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const override { return Vector<uint8_t>(); }
- Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const override { return Vector<uint8_t>(); }
-
- Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const override { return Vector<int>(); }
- Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const override { return Transform3D(); }
-
- void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) override {}
- float voxel_gi_get_dynamic_range(RID p_voxel_gi) const override { return 0; }
-
- void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) override {}
- float voxel_gi_get_propagation(RID p_voxel_gi) const override { return 0; }
-
- void voxel_gi_set_energy(RID p_voxel_gi, float p_range) override {}
- float voxel_gi_get_energy(RID p_voxel_gi) const override { return 0.0; }
-
- void voxel_gi_set_bias(RID p_voxel_gi, float p_range) override {}
- float voxel_gi_get_bias(RID p_voxel_gi) const override { return 0.0; }
-
- void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) override {}
- float voxel_gi_get_normal_bias(RID p_voxel_gi) const override { return 0.0; }
-
- void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) override {}
- bool voxel_gi_is_interior(RID p_voxel_gi) const override { return false; }
-
- void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) override {}
- bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const override { return false; }
-
- void voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) override {}
- float voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const override { return 0; }
-
- uint32_t voxel_gi_get_version(RID p_voxel_gi) override { return 0; }
-
/* OCCLUDER */
void occluder_set_mesh(RID p_occluder, const PackedVector3Array &p_vertices, const PackedInt32Array &p_indices) {}
diff --git a/servers/rendering/environment/renderer_gi.h b/servers/rendering/environment/renderer_gi.h
new file mode 100644
index 0000000000..c4f63b7b6b
--- /dev/null
+++ b/servers/rendering/environment/renderer_gi.h
@@ -0,0 +1,85 @@
+/*************************************************************************/
+/* renderer_gi.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RENDERER_GI_H
+#define RENDERER_GI_H
+
+#include "servers/rendering/renderer_storage.h"
+#include "servers/rendering_server.h"
+
+class RendererGI {
+public:
+ virtual ~RendererGI() {}
+
+ /* VOXEL GI API */
+
+ virtual RID voxel_gi_allocate() = 0;
+ virtual void voxel_gi_free(RID p_rid) = 0;
+ virtual void voxel_gi_initialize(RID p_rid) = 0;
+
+ virtual void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) = 0;
+
+ virtual AABB voxel_gi_get_bounds(RID p_voxel_gi) const = 0;
+ virtual Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const = 0;
+ virtual Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const = 0;
+ virtual Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const = 0;
+ virtual Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const = 0;
+
+ virtual Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const = 0;
+ virtual Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const = 0;
+
+ virtual void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) = 0;
+ virtual float voxel_gi_get_dynamic_range(RID p_voxel_gi) const = 0;
+
+ virtual void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) = 0;
+ virtual float voxel_gi_get_propagation(RID p_voxel_gi) const = 0;
+
+ virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_energy) = 0;
+ virtual float voxel_gi_get_energy(RID p_voxel_gi) const = 0;
+
+ virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_bias) = 0;
+ virtual float voxel_gi_get_bias(RID p_voxel_gi) const = 0;
+
+ virtual void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) = 0;
+ virtual float voxel_gi_get_normal_bias(RID p_voxel_gi) const = 0;
+
+ virtual void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) = 0;
+ virtual bool voxel_gi_is_interior(RID p_voxel_gi) const = 0;
+
+ virtual void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) = 0;
+ virtual bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const = 0;
+
+ virtual void voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) = 0;
+ virtual float voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const = 0;
+
+ virtual uint32_t voxel_gi_get_version(RID p_probe) const = 0;
+};
+
+#endif // !RENDERER_GI_H
diff --git a/servers/rendering/renderer_compositor.h b/servers/rendering/renderer_compositor.h
index df3df1077a..f58bc851ef 100644
--- a/servers/rendering/renderer_compositor.h
+++ b/servers/rendering/renderer_compositor.h
@@ -31,6 +31,7 @@
#ifndef RENDERING_SERVER_COMPOSITOR_H
#define RENDERING_SERVER_COMPOSITOR_H
+#include "servers/rendering/environment/renderer_gi.h"
#include "servers/rendering/renderer_canvas_render.h"
#include "servers/rendering/renderer_scene.h"
#include "servers/rendering/renderer_storage.h"
@@ -81,6 +82,7 @@ public:
virtual RendererMeshStorage *get_mesh_storage() = 0;
virtual RendererParticlesStorage *get_particles_storage() = 0;
virtual RendererTextureStorage *get_texture_storage() = 0;
+ virtual RendererGI *get_gi() = 0;
virtual RendererStorage *get_storage() = 0;
virtual RendererCanvasRender *get_canvas() = 0;
virtual RendererSceneRender *get_scene() = 0;
diff --git a/servers/rendering/renderer_rd/SCsub b/servers/rendering/renderer_rd/SCsub
index 774a6b7951..10b83dca11 100644
--- a/servers/rendering/renderer_rd/SCsub
+++ b/servers/rendering/renderer_rd/SCsub
@@ -5,6 +5,7 @@ Import("env")
env.add_source_files(env.servers_sources, "*.cpp")
SConscript("effects/SCsub")
+SConscript("environment/SCsub")
SConscript("forward_clustered/SCsub")
SConscript("forward_mobile/SCsub")
SConscript("shaders/SCsub")
diff --git a/servers/rendering/renderer_rd/effects/resolve.cpp b/servers/rendering/renderer_rd/effects/resolve.cpp
new file mode 100644
index 0000000000..6c49a2ebce
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/resolve.cpp
@@ -0,0 +1,130 @@
+/*************************************************************************/
+/* resolve.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "resolve.h"
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
+#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
+#include "servers/rendering/renderer_rd/uniform_set_cache_rd.h"
+
+using namespace RendererRD;
+
+Resolve::Resolve() {
+ Vector<String> resolve_modes;
+ resolve_modes.push_back("\n#define MODE_RESOLVE_GI\n");
+ resolve_modes.push_back("\n#define MODE_RESOLVE_GI\n#define VOXEL_GI_RESOLVE\n");
+ resolve_modes.push_back("\n#define MODE_RESOLVE_DEPTH\n");
+
+ resolve.shader.initialize(resolve_modes);
+
+ resolve.shader_version = resolve.shader.version_create();
+
+ for (int i = 0; i < RESOLVE_MODE_MAX; i++) {
+ resolve.pipelines[i] = RD::get_singleton()->compute_pipeline_create(resolve.shader.version_get_shader(resolve.shader_version, i));
+ }
+}
+
+Resolve::~Resolve() {
+ resolve.shader.version_free(resolve.shader_version);
+}
+
+void Resolve::resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_voxel_gi, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_voxel_gi, Vector2i p_screen_size, int p_samples, uint32_t p_barrier) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ ResolvePushConstant push_constant;
+ push_constant.screen_size[0] = p_screen_size.x;
+ push_constant.screen_size[1] = p_screen_size.y;
+ push_constant.samples = p_samples;
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_depth }));
+ RD::Uniform u_source_normal_roughness(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, Vector<RID>({ default_sampler, p_source_normal_roughness }));
+ RD::Uniform u_dest_depth(RD::UNIFORM_TYPE_IMAGE, 0, Vector<RID>({ p_dest_depth }));
+ RD::Uniform u_dest_normal_roughness(RD::UNIFORM_TYPE_IMAGE, 1, Vector<RID>({ p_dest_normal_roughness }));
+
+ ResolveMode mode = p_source_voxel_gi.is_valid() ? RESOLVE_MODE_GI_VOXEL_GI : RESOLVE_MODE_GI;
+ RID shader = resolve.shader.version_get_shader(resolve.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, resolve.pipelines[mode]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_depth, u_source_normal_roughness), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_dest_depth, u_dest_normal_roughness), 1);
+ if (p_source_voxel_gi.is_valid()) {
+ RD::Uniform u_source_voxel_gi(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_voxel_gi }));
+ RD::Uniform u_dest_voxel_gi(RD::UNIFORM_TYPE_IMAGE, 0, p_dest_voxel_gi);
+
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 2, u_source_voxel_gi), 2);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 3, u_dest_voxel_gi), 3);
+ }
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ResolvePushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.x, p_screen_size.y, 1);
+
+ RD::get_singleton()->compute_list_end(p_barrier);
+}
+
+void Resolve::resolve_depth(RID p_source_depth, RID p_dest_depth, Vector2i p_screen_size, int p_samples, uint32_t p_barrier) {
+ UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton();
+ ERR_FAIL_NULL(uniform_set_cache);
+ MaterialStorage *material_storage = MaterialStorage::get_singleton();
+ ERR_FAIL_NULL(material_storage);
+
+ ResolvePushConstant push_constant;
+ push_constant.screen_size[0] = p_screen_size.x;
+ push_constant.screen_size[1] = p_screen_size.y;
+ push_constant.samples = p_samples;
+
+ // setup our uniforms
+ RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
+
+ RD::Uniform u_source_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_source_depth }));
+ RD::Uniform u_dest_depth(RD::UNIFORM_TYPE_IMAGE, 0, p_dest_depth);
+
+ ResolveMode mode = RESOLVE_MODE_DEPTH;
+ RID shader = resolve.shader.version_get_shader(resolve.shader_version, mode);
+ ERR_FAIL_COND(shader.is_null());
+
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, resolve.pipelines[mode]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 0, u_source_depth), 0);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set_cache->get_cache(shader, 1, u_dest_depth), 1);
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ResolvePushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.x, p_screen_size.y, 1);
+
+ RD::get_singleton()->compute_list_end(p_barrier);
+}
diff --git a/servers/rendering/renderer_rd/effects/resolve.h b/servers/rendering/renderer_rd/effects/resolve.h
new file mode 100644
index 0000000000..d4b24a610f
--- /dev/null
+++ b/servers/rendering/renderer_rd/effects/resolve.h
@@ -0,0 +1,74 @@
+/*************************************************************************/
+/* resolve.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef RESOLVE_RD_H
+#define RESOLVE_RD_H
+
+#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
+#include "servers/rendering/renderer_rd/shaders/effects/resolve.glsl.gen.h"
+#include "servers/rendering/renderer_scene_render.h"
+
+#include "servers/rendering_server.h"
+
+namespace RendererRD {
+
+class Resolve {
+private:
+ struct ResolvePushConstant {
+ int32_t screen_size[2];
+ int32_t samples;
+ uint32_t pad;
+ };
+
+ enum ResolveMode {
+ RESOLVE_MODE_GI,
+ RESOLVE_MODE_GI_VOXEL_GI,
+ RESOLVE_MODE_DEPTH,
+ RESOLVE_MODE_MAX
+ };
+
+ struct ResolveShader {
+ ResolvePushConstant push_constant;
+ ResolveShaderRD shader;
+ RID shader_version;
+ RID pipelines[RESOLVE_MODE_MAX]; //3 quality levels
+ } resolve;
+
+public:
+ Resolve();
+ ~Resolve();
+
+ void resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_voxel_gi, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_voxel_gi, Vector2i p_screen_size, int p_samples, uint32_t p_barrier = RD::BARRIER_MASK_ALL);
+ void resolve_depth(RID p_source_depth, RID p_dest_depth, Vector2i p_screen_size, int p_samples, uint32_t p_barrier = RD::BARRIER_MASK_ALL);
+};
+
+} // namespace RendererRD
+
+#endif // !RESOLVE_RD_H
diff --git a/servers/rendering/renderer_rd/effects_rd.cpp b/servers/rendering/renderer_rd/effects_rd.cpp
index bf97c6fbe9..4c542fa2e2 100644
--- a/servers/rendering/renderer_rd/effects_rd.cpp
+++ b/servers/rendering/renderer_rd/effects_rd.cpp
@@ -1396,46 +1396,6 @@ void EffectsRD::cubemap_filter_raster(RID p_source_cubemap, RID p_dest_framebuff
RD::get_singleton()->draw_list_end();
}
-void EffectsRD::resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_voxel_gi, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_voxel_gi, Vector2i p_screen_size, int p_samples, uint32_t p_barrier) {
- ResolvePushConstant push_constant;
- push_constant.screen_size[0] = p_screen_size.x;
- push_constant.screen_size[1] = p_screen_size.y;
- push_constant.samples = p_samples;
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, resolve.pipelines[p_source_voxel_gi.is_valid() ? RESOLVE_MODE_GI_VOXEL_GI : RESOLVE_MODE_GI]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture_pair(p_source_depth, p_source_normal_roughness), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_image_pair(p_dest_depth, p_dest_normal_roughness), 1);
- if (p_source_voxel_gi.is_valid()) {
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_voxel_gi), 2);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_voxel_gi), 3);
- }
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ResolvePushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.x, p_screen_size.y, 1);
-
- RD::get_singleton()->compute_list_end(p_barrier);
-}
-
-void EffectsRD::resolve_depth(RID p_source_depth, RID p_dest_depth, Vector2i p_screen_size, int p_samples, uint32_t p_barrier) {
- ResolvePushConstant push_constant;
- push_constant.screen_size[0] = p_screen_size.x;
- push_constant.screen_size[1] = p_screen_size.y;
- push_constant.samples = p_samples;
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, resolve.pipelines[RESOLVE_MODE_DEPTH]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_depth), 0);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_depth), 1);
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ResolvePushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.x, p_screen_size.y, 1);
-
- RD::get_singleton()->compute_list_end(p_barrier);
-}
-
void EffectsRD::sort_buffer(RID p_uniform_set, int p_size) {
Sort::PushConstant push_constant;
push_constant.total_elements = p_size;
@@ -2009,21 +1969,6 @@ EffectsRD::EffectsRD(bool p_prefer_raster_effects) {
ssil.pipelines[i] = RD::get_singleton()->compute_pipeline_create(ssil.interleave_shader.version_get_shader(ssil.interleave_shader_version, i - SSIL_INTERLEAVE));
}
}
-
- {
- Vector<String> resolve_modes;
- resolve_modes.push_back("\n#define MODE_RESOLVE_GI\n");
- resolve_modes.push_back("\n#define MODE_RESOLVE_GI\n#define VOXEL_GI_RESOLVE\n");
- resolve_modes.push_back("\n#define MODE_RESOLVE_DEPTH\n");
-
- resolve.shader.initialize(resolve_modes);
-
- resolve.shader_version = resolve.shader.version_create();
-
- for (int i = 0; i < RESOLVE_MODE_MAX; i++) {
- resolve.pipelines[i] = RD::get_singleton()->compute_pipeline_create(resolve.shader.version_get_shader(resolve.shader_version, i));
- }
- }
}
{
@@ -2110,7 +2055,6 @@ EffectsRD::~EffectsRD() {
filter.compute_shader.version_free(filter.shader_version);
}
if (!prefer_raster_effects) {
- resolve.shader.version_free(resolve.shader_version);
specular_merge.shader.version_free(specular_merge.shader_version);
ss_effects.downsample_shader.version_free(ss_effects.downsample_shader_version);
ssao.blur_shader.version_free(ssao.blur_shader_version);
diff --git a/servers/rendering/renderer_rd/effects_rd.h b/servers/rendering/renderer_rd/effects_rd.h
index 787873642e..af4ed5a6ae 100644
--- a/servers/rendering/renderer_rd/effects_rd.h
+++ b/servers/rendering/renderer_rd/effects_rd.h
@@ -43,7 +43,6 @@
#include "servers/rendering/renderer_rd/shaders/fsr_upscale.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/luminance_reduce.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/luminance_reduce_raster.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/resolve.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/roughness_limiter.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/screen_space_reflection.glsl.gen.h"
#include "servers/rendering/renderer_rd/shaders/screen_space_reflection_filter.glsl.gen.h"
@@ -579,26 +578,6 @@ private:
RID pipelines[3]; //3 quality levels
} sss;
- struct ResolvePushConstant {
- int32_t screen_size[2];
- int32_t samples;
- uint32_t pad;
- };
-
- enum ResolveMode {
- RESOLVE_MODE_GI,
- RESOLVE_MODE_GI_VOXEL_GI,
- RESOLVE_MODE_DEPTH,
- RESOLVE_MODE_MAX
- };
-
- struct Resolve {
- ResolvePushConstant push_constant;
- ResolveShaderRD shader;
- RID shader_version;
- RID pipelines[RESOLVE_MODE_MAX]; //3 quality levels
- } resolve;
-
enum SortMode {
SORT_MODE_BLOCK,
SORT_MODE_STEP,
@@ -733,9 +712,6 @@ public:
void merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_base, RID p_reflection);
void sub_surface_scattering(RID p_diffuse, RID p_diffuse2, RID p_depth, const CameraMatrix &p_camera, const Size2i &p_screen_size, float p_scale, float p_depth_scale, RS::SubSurfaceScatteringQuality p_quality);
- void resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_voxel_gi, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_voxel_gi, Vector2i p_screen_size, int p_samples, uint32_t p_barrier = RD::BARRIER_MASK_ALL);
- void resolve_depth(RID p_source_depth, RID p_dest_depth, Vector2i p_screen_size, int p_samples, uint32_t p_barrier = RD::BARRIER_MASK_ALL);
-
void sort_buffer(RID p_uniform_set, int p_size);
EffectsRD(bool p_prefer_raster_effects);
diff --git a/servers/rendering/renderer_rd/environment/SCsub b/servers/rendering/renderer_rd/environment/SCsub
new file mode 100644
index 0000000000..86681f9c74
--- /dev/null
+++ b/servers/rendering/renderer_rd/environment/SCsub
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+Import("env")
+
+env.add_source_files(env.servers_sources, "*.cpp")
diff --git a/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp b/servers/rendering/renderer_rd/environment/gi.cpp
index 7aede6bb48..f3be4a7085 100644
--- a/servers/rendering/renderer_rd/renderer_scene_gi_rd.cpp
+++ b/servers/rendering/renderer_rd/environment/gi.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* renderer_scene_gi_rd.cpp */
+/* gi.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,20 +28,365 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "renderer_scene_gi_rd.h"
+#include "gi.h"
#include "core/config/project_settings.h"
+#include "servers/rendering/renderer_rd/renderer_compositor_rd.h"
#include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
+#include "servers/rendering/renderer_rd/renderer_storage_rd.h"
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
#include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"
#include "servers/rendering/rendering_server_default.h"
-const Vector3i RendererSceneGIRD::SDFGI::Cascade::DIRTY_ALL = Vector3i(0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF);
+using namespace RendererRD;
+
+const Vector3i GI::SDFGI::Cascade::DIRTY_ALL = Vector3i(0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF);
+
+GI *GI::singleton = nullptr;
+
+////////////////////////////////////////////////////////////////////////////////
+// VOXEL GI STORAGE
+
+RID GI::voxel_gi_allocate() {
+ return voxel_gi_owner.allocate_rid();
+}
+
+void GI::voxel_gi_free(RID p_voxel_gi) {
+ voxel_gi_allocate_data(p_voxel_gi, Transform3D(), AABB(), Vector3i(), Vector<uint8_t>(), Vector<uint8_t>(), Vector<uint8_t>(), Vector<int>()); //deallocate
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ voxel_gi->dependency.deleted_notify(p_voxel_gi);
+ voxel_gi_owner.free(p_voxel_gi);
+}
+
+void GI::voxel_gi_initialize(RID p_voxel_gi) {
+ voxel_gi_owner.initialize_rid(p_voxel_gi, VoxelGI());
+}
+
+void GI::voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND(!voxel_gi);
+
+ if (voxel_gi->octree_buffer.is_valid()) {
+ RD::get_singleton()->free(voxel_gi->octree_buffer);
+ RD::get_singleton()->free(voxel_gi->data_buffer);
+ if (voxel_gi->sdf_texture.is_valid()) {
+ RD::get_singleton()->free(voxel_gi->sdf_texture);
+ }
+
+ voxel_gi->sdf_texture = RID();
+ voxel_gi->octree_buffer = RID();
+ voxel_gi->data_buffer = RID();
+ voxel_gi->octree_buffer_size = 0;
+ voxel_gi->data_buffer_size = 0;
+ voxel_gi->cell_count = 0;
+ }
+
+ voxel_gi->to_cell_xform = p_to_cell_xform;
+ voxel_gi->bounds = p_aabb;
+ voxel_gi->octree_size = p_octree_size;
+ voxel_gi->level_counts = p_level_counts;
+
+ if (p_octree_cells.size()) {
+ ERR_FAIL_COND(p_octree_cells.size() % 32 != 0); //cells size must be a multiple of 32
+
+ uint32_t cell_count = p_octree_cells.size() / 32;
+
+ ERR_FAIL_COND(p_data_cells.size() != (int)cell_count * 16); //see that data size matches
+
+ voxel_gi->cell_count = cell_count;
+ voxel_gi->octree_buffer = RD::get_singleton()->storage_buffer_create(p_octree_cells.size(), p_octree_cells);
+ voxel_gi->octree_buffer_size = p_octree_cells.size();
+ voxel_gi->data_buffer = RD::get_singleton()->storage_buffer_create(p_data_cells.size(), p_data_cells);
+ voxel_gi->data_buffer_size = p_data_cells.size();
+
+ if (p_distance_field.size()) {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R8_UNORM;
+ tf.width = voxel_gi->octree_size.x;
+ tf.height = voxel_gi->octree_size.y;
+ tf.depth = voxel_gi->octree_size.z;
+ tf.texture_type = RD::TEXTURE_TYPE_3D;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
+ Vector<Vector<uint8_t>> s;
+ s.push_back(p_distance_field);
+ voxel_gi->sdf_texture = RD::get_singleton()->texture_create(tf, RD::TextureView(), s);
+ }
+#if 0
+ {
+ RD::TextureFormat tf;
+ tf.format = RD::DATA_FORMAT_R8_UNORM;
+ tf.width = voxel_gi->octree_size.x;
+ tf.height = voxel_gi->octree_size.y;
+ tf.depth = voxel_gi->octree_size.z;
+ tf.type = RD::TEXTURE_TYPE_3D;
+ tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
+ tf.shareable_formats.push_back(RD::DATA_FORMAT_R8_UNORM);
+ tf.shareable_formats.push_back(RD::DATA_FORMAT_R8_UINT);
+ voxel_gi->sdf_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ }
+ RID shared_tex;
+ {
+ RD::TextureView tv;
+ tv.format_override = RD::DATA_FORMAT_R8_UINT;
+ shared_tex = RD::get_singleton()->texture_create_shared(tv, voxel_gi->sdf_texture);
+ }
+ //update SDF texture
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 1;
+ u.append_id(voxel_gi->octree_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
+ u.binding = 2;
+ u.append_id(voxel_gi->data_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 3;
+ u.append_id(shared_tex);
+ uniforms.push_back(u);
+ }
+
+ RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, voxel_gi_sdf_shader_version_shader, 0);
+
+ {
+ uint32_t push_constant[4] = { 0, 0, 0, 0 };
+
+ for (int i = 0; i < voxel_gi->level_counts.size() - 1; i++) {
+ push_constant[0] += voxel_gi->level_counts[i];
+ }
+ push_constant[1] = push_constant[0] + voxel_gi->level_counts[voxel_gi->level_counts.size() - 1];
+
+ print_line("offset: " + itos(push_constant[0]));
+ print_line("size: " + itos(push_constant[1]));
+ //create SDF
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, voxel_gi_sdf_shader_pipeline);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, push_constant, sizeof(uint32_t) * 4);
+ RD::get_singleton()->compute_list_dispatch(compute_list, voxel_gi->octree_size.x / 4, voxel_gi->octree_size.y / 4, voxel_gi->octree_size.z / 4);
+ RD::get_singleton()->compute_list_end();
+ }
+
+ RD::get_singleton()->free(uniform_set);
+ RD::get_singleton()->free(shared_tex);
+ }
+#endif
+ }
+
+ voxel_gi->version++;
+ voxel_gi->data_version++;
+
+ voxel_gi->dependency.changed_notify(RendererStorage::DEPENDENCY_CHANGED_AABB);
+}
+
+AABB GI::voxel_gi_get_bounds(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, AABB());
+
+ return voxel_gi->bounds;
+}
+
+Vector3i GI::voxel_gi_get_octree_size(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, Vector3i());
+ return voxel_gi->octree_size;
+}
+
+Vector<uint8_t> GI::voxel_gi_get_octree_cells(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, Vector<uint8_t>());
+
+ if (voxel_gi->octree_buffer.is_valid()) {
+ return RD::get_singleton()->buffer_get_data(voxel_gi->octree_buffer);
+ }
+ return Vector<uint8_t>();
+}
+
+Vector<uint8_t> GI::voxel_gi_get_data_cells(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, Vector<uint8_t>());
+
+ if (voxel_gi->data_buffer.is_valid()) {
+ return RD::get_singleton()->buffer_get_data(voxel_gi->data_buffer);
+ }
+ return Vector<uint8_t>();
+}
+
+Vector<uint8_t> GI::voxel_gi_get_distance_field(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, Vector<uint8_t>());
+
+ if (voxel_gi->data_buffer.is_valid()) {
+ return RD::get_singleton()->texture_get_data(voxel_gi->sdf_texture, 0);
+ }
+ return Vector<uint8_t>();
+}
+
+Vector<int> GI::voxel_gi_get_level_counts(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, Vector<int>());
+
+ return voxel_gi->level_counts;
+}
+
+Transform3D GI::voxel_gi_get_to_cell_xform(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, Transform3D());
+
+ return voxel_gi->to_cell_xform;
+}
+
+void GI::voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND(!voxel_gi);
+
+ voxel_gi->dynamic_range = p_range;
+ voxel_gi->version++;
+}
+
+float GI::voxel_gi_get_dynamic_range(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, 0);
+
+ return voxel_gi->dynamic_range;
+}
+
+void GI::voxel_gi_set_propagation(RID p_voxel_gi, float p_range) {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND(!voxel_gi);
+
+ voxel_gi->propagation = p_range;
+ voxel_gi->version++;
+}
+
+float GI::voxel_gi_get_propagation(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, 0);
+ return voxel_gi->propagation;
+}
+
+void GI::voxel_gi_set_energy(RID p_voxel_gi, float p_energy) {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND(!voxel_gi);
+
+ voxel_gi->energy = p_energy;
+}
+
+float GI::voxel_gi_get_energy(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, 0);
+ return voxel_gi->energy;
+}
+
+void GI::voxel_gi_set_bias(RID p_voxel_gi, float p_bias) {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND(!voxel_gi);
+
+ voxel_gi->bias = p_bias;
+}
+
+float GI::voxel_gi_get_bias(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, 0);
+ return voxel_gi->bias;
+}
+
+void GI::voxel_gi_set_normal_bias(RID p_voxel_gi, float p_normal_bias) {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND(!voxel_gi);
+
+ voxel_gi->normal_bias = p_normal_bias;
+}
+
+float GI::voxel_gi_get_normal_bias(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, 0);
+ return voxel_gi->normal_bias;
+}
+
+void GI::voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND(!voxel_gi);
+
+ voxel_gi->anisotropy_strength = p_strength;
+}
+
+float GI::voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, 0);
+ return voxel_gi->anisotropy_strength;
+}
+
+void GI::voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND(!voxel_gi);
+
+ voxel_gi->interior = p_enable;
+}
+
+void GI::voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND(!voxel_gi);
+
+ voxel_gi->use_two_bounces = p_enable;
+ voxel_gi->version++;
+}
+
+bool GI::voxel_gi_is_using_two_bounces(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, false);
+ return voxel_gi->use_two_bounces;
+}
+
+bool GI::voxel_gi_is_interior(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, 0);
+ return voxel_gi->interior;
+}
+
+uint32_t GI::voxel_gi_get_version(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, 0);
+ return voxel_gi->version;
+}
+
+uint32_t GI::voxel_gi_get_data_version(RID p_voxel_gi) {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, 0);
+ return voxel_gi->data_version;
+}
+
+RID GI::voxel_gi_get_octree_buffer(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, RID());
+ return voxel_gi->octree_buffer;
+}
+
+RID GI::voxel_gi_get_data_buffer(RID p_voxel_gi) const {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, RID());
+ return voxel_gi->data_buffer;
+}
+
+RID GI::voxel_gi_get_sdf_texture(RID p_voxel_gi) {
+ VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
+ ERR_FAIL_COND_V(!voxel_gi, RID());
+
+ return voxel_gi->sdf_texture;
+}
////////////////////////////////////////////////////////////////////////////////
// SDFGI
-void RendererSceneGIRD::SDFGI::create(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, RendererSceneGIRD *p_gi) {
+void GI::SDFGI::create(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, GI *p_gi) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
@@ -755,7 +1100,7 @@ void RendererSceneGIRD::SDFGI::create(RendererSceneEnvironmentRD *p_env, const V
reads_sky = p_env->sdfgi_read_sky_light;
}
-void RendererSceneGIRD::SDFGI::erase() {
+void GI::SDFGI::erase() {
for (uint32_t i = 0; i < cascades.size(); i++) {
const SDFGI::Cascade &c = cascades[i];
RD::get_singleton()->free(c.light_data);
@@ -791,9 +1136,26 @@ void RendererSceneGIRD::SDFGI::erase() {
RD::get_singleton()->free(ambient_texture);
RD::get_singleton()->free(cascades_ubo);
+
+ for (uint32_t v = 0; v < RendererSceneRender::MAX_RENDER_VIEWS; v++) {
+ if (RD::get_singleton()->uniform_set_is_valid(debug_uniform_set[v])) {
+ RD::get_singleton()->free(debug_uniform_set[v]);
+ }
+ debug_uniform_set[v] = RID();
+ }
+
+ if (RD::get_singleton()->uniform_set_is_valid(debug_probes_uniform_set)) {
+ RD::get_singleton()->free(debug_probes_uniform_set);
+ }
+ debug_probes_uniform_set = RID();
+
+ if (debug_probes_scene_data_ubo.is_valid()) {
+ RD::get_singleton()->free(debug_probes_scene_data_ubo);
+ debug_probes_scene_data_ubo = RID();
+ }
}
-void RendererSceneGIRD::SDFGI::update(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position) {
+void GI::SDFGI::update(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position) {
bounce_feedback = p_env->sdfgi_bounce_feedback;
energy = p_env->sdfgi_energy;
normal_bias = p_env->sdfgi_normal_bias;
@@ -851,7 +1213,7 @@ void RendererSceneGIRD::SDFGI::update(RendererSceneEnvironmentRD *p_env, const V
}
}
-void RendererSceneGIRD::SDFGI::update_light() {
+void GI::SDFGI::update_light() {
RD::get_singleton()->draw_command_begin_label("SDFGI Update dynamic Light");
/* Update dynamic light */
@@ -898,7 +1260,7 @@ void RendererSceneGIRD::SDFGI::update_light() {
RD::get_singleton()->draw_command_end_label();
}
-void RendererSceneGIRD::SDFGI::update_probes(RendererSceneEnvironmentRD *p_env, RendererSceneSkyRD::Sky *p_sky) {
+void GI::SDFGI::update_probes(RendererSceneEnvironmentRD *p_env, RendererSceneSkyRD::Sky *p_sky) {
RD::get_singleton()->draw_command_begin_label("SDFGI Update Probes");
SDFGIShader::IntegratePushConstant push_constant;
@@ -990,7 +1352,7 @@ void RendererSceneGIRD::SDFGI::update_probes(RendererSceneEnvironmentRD *p_env,
RD::get_singleton()->draw_command_end_label();
}
-void RendererSceneGIRD::SDFGI::store_probes() {
+void GI::SDFGI::store_probes() {
RD::get_singleton()->barrier(RD::BARRIER_MASK_COMPUTE, RD::BARRIER_MASK_COMPUTE);
RD::get_singleton()->draw_command_begin_label("SDFGI Store Probes");
@@ -1035,7 +1397,7 @@ void RendererSceneGIRD::SDFGI::store_probes() {
RD::get_singleton()->draw_command_end_label();
}
-int RendererSceneGIRD::SDFGI::get_pending_region_data(int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds) const {
+int GI::SDFGI::get_pending_region_data(int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds) const {
int dirty_count = 0;
for (uint32_t i = 0; i < cascades.size(); i++) {
const SDFGI::Cascade &c = cascades[i];
@@ -1091,7 +1453,7 @@ int RendererSceneGIRD::SDFGI::get_pending_region_data(int p_region, Vector3i &r_
return -1;
}
-void RendererSceneGIRD::SDFGI::update_cascades() {
+void GI::SDFGI::update_cascades() {
//update cascades
SDFGI::Cascade::UBO cascade_data[SDFGI::MAX_CASCADES];
int32_t probe_divisor = cascade_size / SDFGI::PROBE_DIVISOR;
@@ -1112,160 +1474,177 @@ void RendererSceneGIRD::SDFGI::update_cascades() {
RD::get_singleton()->buffer_update(cascades_ubo, 0, sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES, cascade_data, RD::BARRIER_MASK_COMPUTE);
}
-void RendererSceneGIRD::SDFGI::debug_draw(const CameraMatrix &p_projection, const Transform3D &p_transform, int p_width, int p_height, RID p_render_target, RID p_texture) {
+void GI::SDFGI::debug_draw(uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, int p_width, int p_height, RID p_render_target, RID p_texture, const Vector<RID> &p_texture_views) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
RendererRD::CopyEffects *copy_effects = RendererRD::CopyEffects::get_singleton();
- if (!debug_uniform_set.is_valid() || !RD::get_singleton()->uniform_set_is_valid(debug_uniform_set)) {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.binding = 1;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
- if (i < cascades.size()) {
- u.append_id(cascades[i].sdf_tex);
- } else {
- u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ for (uint32_t v = 0; v < p_view_count; v++) {
+ if (!debug_uniform_set[v].is_valid() || !RD::get_singleton()->uniform_set_is_valid(debug_uniform_set[v])) {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.binding = 1;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
+ if (i < cascades.size()) {
+ u.append_id(cascades[i].sdf_tex);
+ } else {
+ u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
}
+ uniforms.push_back(u);
}
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 2;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
- if (i < cascades.size()) {
- u.append_id(cascades[i].light_tex);
- } else {
- u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ {
+ RD::Uniform u;
+ u.binding = 2;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
+ if (i < cascades.size()) {
+ u.append_id(cascades[i].light_tex);
+ } else {
+ u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
}
+ uniforms.push_back(u);
}
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 3;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
- if (i < cascades.size()) {
- u.append_id(cascades[i].light_aniso_0_tex);
- } else {
- u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ {
+ RD::Uniform u;
+ u.binding = 3;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
+ if (i < cascades.size()) {
+ u.append_id(cascades[i].light_aniso_0_tex);
+ } else {
+ u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
}
+ uniforms.push_back(u);
}
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 4;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
- if (i < cascades.size()) {
- u.append_id(cascades[i].light_aniso_1_tex);
- } else {
- u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ {
+ RD::Uniform u;
+ u.binding = 4;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) {
+ if (i < cascades.size()) {
+ u.append_id(cascades[i].light_aniso_1_tex);
+ } else {
+ u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
}
+ uniforms.push_back(u);
}
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 5;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.append_id(occlusion_texture);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 8;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 9;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.append_id(cascades_ubo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 10;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.append_id(p_texture);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 11;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.append_id(lightprobe_texture);
- uniforms.push_back(u);
+ {
+ RD::Uniform u;
+ u.binding = 5;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.append_id(occlusion_texture);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 8;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 9;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.append_id(cascades_ubo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 10;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.append_id(p_texture_views[v]);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 11;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.append_id(lightprobe_texture);
+ uniforms.push_back(u);
+ }
+ debug_uniform_set[v] = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.debug_shader_version, 0);
}
- debug_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.debug_shader_version, 0);
- }
-
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.debug_pipeline);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, debug_uniform_set, 0);
-
- SDFGIShader::DebugPushConstant push_constant;
- push_constant.grid_size[0] = cascade_size;
- push_constant.grid_size[1] = cascade_size;
- push_constant.grid_size[2] = cascade_size;
- push_constant.max_cascades = cascades.size();
- push_constant.screen_size[0] = p_width;
- push_constant.screen_size[1] = p_height;
- push_constant.probe_axis_size = probe_axis_count;
- push_constant.use_occlusion = uses_occlusion;
- push_constant.y_mult = y_mult;
- Vector2 vp_half = p_projection.get_viewport_half_extents();
- push_constant.cam_extent[0] = vp_half.x;
- push_constant.cam_extent[1] = vp_half.y;
- push_constant.cam_extent[2] = -p_projection.get_z_near();
-
- push_constant.cam_transform[0] = p_transform.basis.rows[0][0];
- push_constant.cam_transform[1] = p_transform.basis.rows[1][0];
- push_constant.cam_transform[2] = p_transform.basis.rows[2][0];
- push_constant.cam_transform[3] = 0;
- push_constant.cam_transform[4] = p_transform.basis.rows[0][1];
- push_constant.cam_transform[5] = p_transform.basis.rows[1][1];
- push_constant.cam_transform[6] = p_transform.basis.rows[2][1];
- push_constant.cam_transform[7] = 0;
- push_constant.cam_transform[8] = p_transform.basis.rows[0][2];
- push_constant.cam_transform[9] = p_transform.basis.rows[1][2];
- push_constant.cam_transform[10] = p_transform.basis.rows[2][2];
- push_constant.cam_transform[11] = 0;
- push_constant.cam_transform[12] = p_transform.origin.x;
- push_constant.cam_transform[13] = p_transform.origin.y;
- push_constant.cam_transform[14] = p_transform.origin.z;
- push_constant.cam_transform[15] = 1;
-
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::DebugPushConstant));
-
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_width, p_height, 1);
- RD::get_singleton()->compute_list_end();
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.debug_pipeline);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, debug_uniform_set[v], 0);
+
+ SDFGIShader::DebugPushConstant push_constant;
+ push_constant.grid_size[0] = cascade_size;
+ push_constant.grid_size[1] = cascade_size;
+ push_constant.grid_size[2] = cascade_size;
+ push_constant.max_cascades = cascades.size();
+ push_constant.screen_size[0] = p_width;
+ push_constant.screen_size[1] = p_height;
+ push_constant.probe_axis_size = probe_axis_count;
+ push_constant.use_occlusion = uses_occlusion;
+ push_constant.y_mult = y_mult;
+
+ push_constant.z_near = -p_projections[v].get_z_near();
+
+ push_constant.cam_transform[0] = p_transform.basis.rows[0][0];
+ push_constant.cam_transform[1] = p_transform.basis.rows[1][0];
+ push_constant.cam_transform[2] = p_transform.basis.rows[2][0];
+ push_constant.cam_transform[3] = 0;
+ push_constant.cam_transform[4] = p_transform.basis.rows[0][1];
+ push_constant.cam_transform[5] = p_transform.basis.rows[1][1];
+ push_constant.cam_transform[6] = p_transform.basis.rows[2][1];
+ push_constant.cam_transform[7] = 0;
+ push_constant.cam_transform[8] = p_transform.basis.rows[0][2];
+ push_constant.cam_transform[9] = p_transform.basis.rows[1][2];
+ push_constant.cam_transform[10] = p_transform.basis.rows[2][2];
+ push_constant.cam_transform[11] = 0;
+ push_constant.cam_transform[12] = p_transform.origin.x;
+ push_constant.cam_transform[13] = p_transform.origin.y;
+ push_constant.cam_transform[14] = p_transform.origin.z;
+ push_constant.cam_transform[15] = 1;
+
+ // need to properly unproject for asymmetric projection matrices in stereo..
+ CameraMatrix inv_projection = p_projections[v].inverse();
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ push_constant.inv_projection[i * 4 + j] = inv_projection.matrix[i][j];
+ }
+ }
+
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::DebugPushConstant));
+
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_width, p_height, 1);
+ RD::get_singleton()->compute_list_end();
+ }
Size2 rtsize = texture_storage->render_target_get_size(p_render_target);
- copy_effects->copy_to_fb_rect(p_texture, texture_storage->render_target_get_rd_framebuffer(p_render_target), Rect2(Vector2(), rtsize), true);
+ copy_effects->copy_to_fb_rect(p_texture, texture_storage->render_target_get_rd_framebuffer(p_render_target), Rect2(Vector2(), rtsize), true, false, false, false, RID(), p_view_count > 1);
}
-void RendererSceneGIRD::SDFGI::debug_probes(RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform) {
+void GI::SDFGI::debug_probes(RID p_framebuffer, const uint32_t p_view_count, const CameraMatrix *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth) {
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
- SDFGIShader::DebugProbesPushConstant push_constant;
+ // setup scene data
+ {
+ SDFGIShader::DebugProbesSceneData scene_data;
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
- push_constant.projection[i * 4 + j] = p_camera_with_transform.matrix[i][j];
+ if (debug_probes_scene_data_ubo.is_null()) {
+ debug_probes_scene_data_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SDFGIShader::DebugProbesSceneData));
}
+
+ for (uint32_t v = 0; v < p_view_count; v++) {
+ RendererStorageRD::store_camera(p_camera_with_transforms[v], scene_data.projection[v]);
+ }
+
+ RD::get_singleton()->buffer_update(debug_probes_scene_data_ubo, 0, sizeof(SDFGIShader::DebugProbesSceneData), &scene_data, RD::BARRIER_MASK_RASTER);
}
+ // setup push constant
+ SDFGIShader::DebugProbesPushConstant push_constant;
+
//gen spheres from strips
uint32_t band_points = 16;
push_constant.band_power = 4;
@@ -1314,14 +1693,26 @@ void RendererSceneGIRD::SDFGI::debug_probes(RD::DrawListID p_draw_list, RID p_fr
u.append_id(occlusion_texture);
uniforms.push_back(u);
}
+ {
+ RD::Uniform u;
+ u.binding = 5;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.append_id(debug_probes_scene_data_ubo);
+ uniforms.push_back(u);
+ }
debug_probes_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi->sdfgi_shader.debug_probes.version_get_shader(gi->sdfgi_shader.debug_probes_shader, 0), 0);
}
- RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, gi->sdfgi_shader.debug_probes_pipeline[SDFGIShader::PROBE_DEBUG_PROBES].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, debug_probes_uniform_set, 0);
- RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(SDFGIShader::DebugProbesPushConstant));
- RD::get_singleton()->draw_list_draw(p_draw_list, false, total_probes, total_points);
+ SDFGIShader::ProbeDebugMode mode = p_view_count > 1 ? SDFGIShader::PROBE_DEBUG_PROBES_MULTIVIEW : SDFGIShader::PROBE_DEBUG_PROBES;
+
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CONTINUE, p_will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, p_will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ);
+ RD::get_singleton()->draw_command_begin_label("Debug SDFGI");
+
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, gi->sdfgi_shader.debug_probes_pipeline[mode].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, debug_probes_uniform_set, 0);
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(SDFGIShader::DebugProbesPushConstant));
+ RD::get_singleton()->draw_list_draw(draw_list, false, total_probes, total_points);
if (gi->sdfgi_debug_probe_dir != Vector3()) {
uint32_t cascade = 0;
@@ -1373,14 +1764,17 @@ void RendererSceneGIRD::SDFGI::debug_probes(RD::DrawListID p_draw_list, RID p_fr
uint32_t cell_count = probe_cells * 2 * probe_cells * 2 * probe_cells * 2;
- RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, gi->sdfgi_shader.debug_probes_pipeline[SDFGIShader::PROBE_DEBUG_VISIBILITY].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
- RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, debug_probes_uniform_set, 0);
- RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(SDFGIShader::DebugProbesPushConstant));
- RD::get_singleton()->draw_list_draw(p_draw_list, false, cell_count, total_points);
+ RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, gi->sdfgi_shader.debug_probes_pipeline[p_view_count > 1 ? SDFGIShader::PROBE_DEBUG_VISIBILITY_MULTIVIEW : SDFGIShader::PROBE_DEBUG_VISIBILITY].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer)));
+ RD::get_singleton()->draw_list_bind_uniform_set(draw_list, debug_probes_uniform_set, 0);
+ RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(SDFGIShader::DebugProbesPushConstant));
+ RD::get_singleton()->draw_list_draw(draw_list, false, cell_count, total_points);
}
+
+ RD::get_singleton()->draw_command_end_label();
+ RD::get_singleton()->draw_list_end();
}
-void RendererSceneGIRD::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_render_data, RendererSceneRenderRD *p_scene_render) {
+void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_render_data, RendererSceneRenderRD *p_scene_render) {
/* Update general SDFGI Buffer */
SDFGIData sdfgi_data;
@@ -1545,7 +1939,7 @@ void RendererSceneGIRD::SDFGI::pre_process_gi(const Transform3D &p_transform, Re
}
}
-void RendererSceneGIRD::SDFGI::render_region(RID p_render_buffers, int p_region, const PagedArray<RendererSceneRender::GeometryInstance *> &p_instances, RendererSceneRenderRD *p_scene_render) {
+void GI::SDFGI::render_region(RID p_render_buffers, int p_region, const PagedArray<RendererSceneRender::GeometryInstance *> &p_instances, RendererSceneRenderRD *p_scene_render) {
//print_line("rendering region " + itos(p_region));
RendererSceneRenderRD::RenderBuffers *rb = p_scene_render->render_buffers_owner.get_or_null(p_render_buffers);
ERR_FAIL_COND(!rb); // we wouldn't be here if this failed but...
@@ -1904,7 +2298,7 @@ void RendererSceneGIRD::SDFGI::render_region(RID p_render_buffers, int p_region,
}
}
-void RendererSceneGIRD::SDFGI::render_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result, RendererSceneRenderRD *p_scene_render) {
+void GI::SDFGI::render_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const PagedArray<RID> *p_positional_light_cull_result, RendererSceneRenderRD *p_scene_render) {
RendererSceneRenderRD::RenderBuffers *rb = p_scene_render->render_buffers_owner.get_or_null(p_render_buffers);
ERR_FAIL_COND(!rb); // we wouldn't be here if this failed but...
@@ -2026,10 +2420,10 @@ void RendererSceneGIRD::SDFGI::render_static_lights(RID p_render_buffers, uint32
////////////////////////////////////////////////////////////////////////////////
// VoxelGIInstance
-void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects, RendererSceneRenderRD *p_scene_render) {
+void GI::VoxelGIInstance::update(bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects, RendererSceneRenderRD *p_scene_render) {
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
- uint32_t data_version = storage->voxel_gi_get_data_version(probe);
+ uint32_t data_version = gi->voxel_gi_get_data_version(probe);
// (RE)CREATE IF NEEDED
@@ -2048,11 +2442,11 @@ void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, c
dynamic_maps.clear();
- Vector3i octree_size = storage->voxel_gi_get_octree_size(probe);
+ Vector3i octree_size = gi->voxel_gi_get_octree_size(probe);
if (octree_size != Vector3i()) {
//can create a 3D texture
- Vector<int> levels = storage->voxel_gi_get_level_counts(probe);
+ Vector<int> levels = gi->voxel_gi_get_level_counts(probe);
RD::TextureFormat tf;
tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
@@ -2092,14 +2486,14 @@ void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, c
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 1;
- u.append_id(storage->voxel_gi_get_octree_buffer(probe));
+ u.append_id(gi->voxel_gi_get_octree_buffer(probe));
uniforms.push_back(u);
}
{
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 2;
- u.append_id(storage->voxel_gi_get_data_buffer(probe));
+ u.append_id(gi->voxel_gi_get_data_buffer(probe));
uniforms.push_back(u);
}
@@ -2114,7 +2508,7 @@ void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, c
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 9;
- u.append_id(storage->voxel_gi_get_sdf_texture(probe));
+ u.append_id(gi->voxel_gi_get_sdf_texture(probe));
uniforms.push_back(u);
}
{
@@ -2268,7 +2662,7 @@ void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, c
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 9;
- u.append_id(storage->voxel_gi_get_sdf_texture(probe));
+ u.append_id(gi->voxel_gi_get_sdf_texture(probe));
uniforms.push_back(u);
}
{
@@ -2337,7 +2731,7 @@ void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, c
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 9;
- u.append_id(storage->voxel_gi_get_sdf_texture(probe));
+ u.append_id(gi->voxel_gi_get_sdf_texture(probe));
uniforms.push_back(u);
}
{
@@ -2388,7 +2782,7 @@ void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, c
light_count = MIN(gi->voxel_gi_max_lights, (uint32_t)p_light_instances.size());
{
- Transform3D to_cell = storage->voxel_gi_get_to_cell_xform(probe);
+ Transform3D to_cell = gi->voxel_gi_get_to_cell_xform(probe);
Transform3D to_probe_xform = (transform * to_cell.affine_inverse()).affine_inverse();
//update lights
@@ -2439,7 +2833,7 @@ void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, c
if (mipmaps.size()) {
//can update mipmaps
- Vector3i probe_size = storage->voxel_gi_get_octree_size(probe);
+ Vector3i probe_size = gi->voxel_gi_get_octree_size(probe);
VoxelGIPushConstant push_constant;
@@ -2448,8 +2842,8 @@ void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, c
push_constant.limits[2] = probe_size.z;
push_constant.stack_size = mipmaps.size();
push_constant.emission_scale = 1.0;
- push_constant.propagation = storage->voxel_gi_get_propagation(probe);
- push_constant.dynamic_range = storage->voxel_gi_get_dynamic_range(probe);
+ push_constant.propagation = gi->voxel_gi_get_propagation(probe);
+ push_constant.dynamic_range = gi->voxel_gi_get_dynamic_range(probe);
push_constant.light_count = light_count;
push_constant.aniso_strength = 0;
@@ -2461,7 +2855,7 @@ void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, c
int passes;
if (p_update_light_instances) {
- passes = storage->voxel_gi_is_using_two_bounces(probe) ? 2 : 1;
+ passes = gi->voxel_gi_is_using_two_bounces(probe) ? 2 : 1;
} else {
passes = 1; //only re-blitting is necessary
}
@@ -2528,13 +2922,13 @@ void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, c
has_dynamic_object_data = false; //clear until dynamic object data is used again
if (p_dynamic_objects.size() && dynamic_maps.size()) {
- Vector3i octree_size = storage->voxel_gi_get_octree_size(probe);
+ Vector3i octree_size = gi->voxel_gi_get_octree_size(probe);
int multiplier = dynamic_maps[0].size / MAX(MAX(octree_size.x, octree_size.y), octree_size.z);
Transform3D oversample_scale;
oversample_scale.basis.scale(Vector3(multiplier, multiplier, multiplier));
- Transform3D to_cell = oversample_scale * storage->voxel_gi_get_to_cell_xform(probe);
+ Transform3D to_cell = oversample_scale * gi->voxel_gi_get_to_cell_xform(probe);
Transform3D to_world_xform = transform * to_cell.affine_inverse();
Transform3D to_probe_xform = to_world_xform.affine_inverse();
@@ -2634,7 +3028,7 @@ void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, c
push_constant.z_base = xform.origin[z_axis];
push_constant.z_sign = (z_flip ? -1.0 : 1.0);
push_constant.pos_multiplier = float(1.0) / multiplier;
- push_constant.dynamic_range = storage->voxel_gi_get_dynamic_range(probe);
+ push_constant.dynamic_range = gi->voxel_gi_get_dynamic_range(probe);
push_constant.flip_x = x_flip;
push_constant.flip_y = y_flip;
push_constant.rect_pos[0] = rect.position[0];
@@ -2646,7 +3040,7 @@ void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, c
push_constant.prev_rect_size[0] = 0;
push_constant.prev_rect_size[1] = 0;
push_constant.on_mipmap = false;
- push_constant.propagation = storage->voxel_gi_get_propagation(probe);
+ push_constant.propagation = gi->voxel_gi_get_propagation(probe);
push_constant.pad[0] = 0;
push_constant.pad[1] = 0;
push_constant.pad[2] = 0;
@@ -2728,24 +3122,24 @@ void RendererSceneGIRD::VoxelGIInstance::update(bool p_update_light_instances, c
has_dynamic_object_data = true; //clear until dynamic object data is used again
}
- last_probe_version = storage->voxel_gi_get_version(probe);
+ last_probe_version = gi->voxel_gi_get_version(probe);
}
-void RendererSceneGIRD::VoxelGIInstance::debug(RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha) {
+void GI::VoxelGIInstance::debug(RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha) {
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
if (mipmaps.size() == 0) {
return;
}
- CameraMatrix cam_transform = (p_camera_with_transform * CameraMatrix(transform)) * CameraMatrix(storage->voxel_gi_get_to_cell_xform(probe).affine_inverse());
+ CameraMatrix cam_transform = (p_camera_with_transform * CameraMatrix(transform)) * CameraMatrix(gi->voxel_gi_get_to_cell_xform(probe).affine_inverse());
int level = 0;
- Vector3i octree_size = storage->voxel_gi_get_octree_size(probe);
+ Vector3i octree_size = gi->voxel_gi_get_octree_size(probe);
VoxelGIDebugPushConstant push_constant;
push_constant.alpha = p_alpha;
- push_constant.dynamic_range = storage->voxel_gi_get_dynamic_range(probe);
+ push_constant.dynamic_range = gi->voxel_gi_get_dynamic_range(probe);
push_constant.cell_offset = mipmaps[level].cell_offset;
push_constant.level = level;
@@ -2768,7 +3162,7 @@ void RendererSceneGIRD::VoxelGIInstance::debug(RD::DrawListID p_draw_list, RID p
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
u.binding = 1;
- u.append_id(storage->voxel_gi_get_data_buffer(probe));
+ u.append_id(gi->voxel_gi_get_data_buffer(probe));
uniforms.push_back(u);
}
{
@@ -2810,18 +3204,21 @@ void RendererSceneGIRD::VoxelGIInstance::debug(RD::DrawListID p_draw_list, RID p
}
////////////////////////////////////////////////////////////////////////////////
-// GIRD
+// GI
+
+GI::GI() {
+ singleton = this;
-RendererSceneGIRD::RendererSceneGIRD() {
sdfgi_ray_count = RS::EnvironmentSDFGIRayCount(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/sdfgi/probe_ray_count")), 0, int32_t(RS::ENV_SDFGI_RAY_COUNT_MAX - 1)));
sdfgi_frames_to_converge = RS::EnvironmentSDFGIFramesToConverge(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/sdfgi/frames_to_converge")), 0, int32_t(RS::ENV_SDFGI_CONVERGE_MAX - 1)));
sdfgi_frames_to_update_light = RS::EnvironmentSDFGIFramesToUpdateLight(CLAMP(int32_t(GLOBAL_GET("rendering/global_illumination/sdfgi/frames_to_update_lights")), 0, int32_t(RS::ENV_SDFGI_UPDATE_LIGHT_MAX - 1)));
}
-RendererSceneGIRD::~RendererSceneGIRD() {
+GI::~GI() {
+ singleton = nullptr;
}
-void RendererSceneGIRD::init(RendererStorageRD *p_storage, RendererSceneSkyRD *p_sky) {
+void GI::init(RendererStorageRD *p_storage, RendererSceneSkyRD *p_sky) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
@@ -2962,17 +3359,38 @@ void RendererSceneGIRD::init(RendererStorageRD *p_storage, RendererSceneSkyRD *p
//calculate tables
String defines = "\n#define SDFGI_OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n";
Vector<String> gi_modes;
- gi_modes.push_back("\n#define USE_VOXEL_GI_INSTANCES\n");
- gi_modes.push_back("\n#define USE_SDFGI\n");
- gi_modes.push_back("\n#define USE_SDFGI\n\n#define USE_VOXEL_GI_INSTANCES\n");
- gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_VOXEL_GI_INSTANCES\n");
- gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_SDFGI\n");
- gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_SDFGI\n\n#define USE_VOXEL_GI_INSTANCES\n");
+ gi_modes.push_back("\n#define USE_VOXEL_GI_INSTANCES\n"); // MODE_VOXEL_GI
+ gi_modes.push_back("\n#define USE_SDFGI\n"); // MODE_SDFGI
+ gi_modes.push_back("\n#define USE_SDFGI\n\n#define USE_VOXEL_GI_INSTANCES\n"); // MODE_COMBINED
+ gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_VOXEL_GI_INSTANCES\n"); // MODE_HALF_RES_VOXEL_GI
+ gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_SDFGI\n"); // MODE_HALF_RES_SDFGI
+ gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_SDFGI\n\n#define USE_VOXEL_GI_INSTANCES\n"); // MODE_HALF_RES_COMBINED
+
+ gi_modes.push_back("\n#define USE_VOXEL_GI_INSTANCES\n#define USE_MULTIVIEW\n"); // MODE_VOXEL_GI_MULTIVIEW
+ gi_modes.push_back("\n#define USE_SDFGI\n#define USE_MULTIVIEW\n"); // MODE_SDFGI_MULTIVIEW
+ gi_modes.push_back("\n#define USE_SDFGI\n\n#define USE_VOXEL_GI_INSTANCES\n#define USE_MULTIVIEW\n"); // MODE_COMBINED_MULTIVIEW
+ gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_VOXEL_GI_INSTANCES\n#define USE_MULTIVIEW\n"); // MODE_HALF_RES_VOXEL_GI_MULTIVIEW
+ gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_SDFGI\n#define USE_MULTIVIEW\n"); // MODE_HALF_RES_SDFGI_MULTIVIEW
+ gi_modes.push_back("\n#define MODE_HALF_RES\n#define USE_SDFGI\n\n#define USE_VOXEL_GI_INSTANCES\n#define USE_MULTIVIEW\n"); // MODE_HALF_RES_COMBINED_MULTIVIEW
shader.initialize(gi_modes, defines);
+
+ if (!RendererCompositorRD::singleton->is_xr_enabled()) {
+ shader.set_variant_enabled(MODE_VOXEL_GI_MULTIVIEW, false);
+ shader.set_variant_enabled(MODE_SDFGI_MULTIVIEW, false);
+ shader.set_variant_enabled(MODE_COMBINED_MULTIVIEW, false);
+ shader.set_variant_enabled(MODE_HALF_RES_VOXEL_GI_MULTIVIEW, false);
+ shader.set_variant_enabled(MODE_HALF_RES_SDFGI_MULTIVIEW, false);
+ shader.set_variant_enabled(MODE_HALF_RES_COMBINED_MULTIVIEW, false);
+ }
+
shader_version = shader.version_create();
for (int i = 0; i < MODE_MAX; i++) {
- pipelines[i] = RD::get_singleton()->compute_pipeline_create(shader.version_get_shader(shader_version, i));
+ if (shader.is_variant_enabled(i)) {
+ pipelines[i] = RD::get_singleton()->compute_pipeline_create(shader.version_get_shader(shader_version, i));
+ } else {
+ pipelines[i] = RID();
+ }
}
sdfgi_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SDFGIData));
@@ -2991,9 +3409,14 @@ void RendererSceneGIRD::init(RendererStorageRD *p_storage, RendererSceneSkyRD *p
Vector<String> versions;
versions.push_back("\n#define MODE_PROBES\n");
+ versions.push_back("\n#define MODE_PROBES\n#define USE_MULTIVIEW\n");
versions.push_back("\n#define MODE_VISIBILITY\n");
+ versions.push_back("\n#define MODE_VISIBILITY\n#define USE_MULTIVIEW\n");
sdfgi_shader.debug_probes.initialize(versions, defines);
+
+ // TODO disable multiview versions if turned off
+
sdfgi_shader.debug_probes_shader = sdfgi_shader.debug_probes.version_create();
{
@@ -3004,6 +3427,8 @@ void RendererSceneGIRD::init(RendererStorageRD *p_storage, RendererSceneSkyRD *p
ds.enable_depth_write = true;
ds.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL;
for (int i = 0; i < SDFGIShader::PROBE_DEBUG_MAX; i++) {
+ // TODO check if version is enabled
+
RID debug_probes_shader_version = sdfgi_shader.debug_probes.version_get_shader(sdfgi_shader.debug_probes_shader, i);
sdfgi_shader.debug_probes_pipeline[i].setup(debug_probes_shader_version, RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0);
}
@@ -3013,7 +3438,7 @@ void RendererSceneGIRD::init(RendererStorageRD *p_storage, RendererSceneSkyRD *p
half_resolution = GLOBAL_GET("rendering/global_illumination/gi/use_half_resolution");
}
-void RendererSceneGIRD::free() {
+void GI::free() {
RD::get_singleton()->free(default_voxel_gi_buffer);
RD::get_singleton()->free(voxel_gi_lights_uniform);
RD::get_singleton()->free(sdfgi_ubo);
@@ -3032,7 +3457,7 @@ void RendererSceneGIRD::free() {
}
}
-RendererSceneGIRD::SDFGI *RendererSceneGIRD::create_sdfgi(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size) {
+GI::SDFGI *GI::create_sdfgi(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size) {
SDFGI *sdfgi = memnew(SDFGI);
sdfgi->create(p_env, p_world_position, p_requested_history_size, this);
@@ -3040,7 +3465,7 @@ RendererSceneGIRD::SDFGI *RendererSceneGIRD::create_sdfgi(RendererSceneEnvironme
return sdfgi;
}
-void RendererSceneGIRD::setup_voxel_gi_instances(RID p_render_buffers, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used, RendererSceneRenderRD *p_scene_render) {
+void GI::setup_voxel_gi_instances(RID p_render_buffers, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used, RendererSceneRenderRD *p_scene_render) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
r_voxel_gi_instances_used = 0;
@@ -3069,7 +3494,7 @@ void RendererSceneGIRD::setup_voxel_gi_instances(RID p_render_buffers, const Tra
RID base_probe = gipi->probe;
- Transform3D to_cell = storage->voxel_gi_get_to_cell_xform(gipi->probe) * gipi->transform.affine_inverse() * to_camera;
+ Transform3D to_cell = voxel_gi_get_to_cell_xform(gipi->probe) * gipi->transform.affine_inverse() * to_camera;
gipd.xform[0] = to_cell.basis.rows[0][0];
gipd.xform[1] = to_cell.basis.rows[1][0];
@@ -3088,16 +3513,16 @@ void RendererSceneGIRD::setup_voxel_gi_instances(RID p_render_buffers, const Tra
gipd.xform[14] = to_cell.origin.z;
gipd.xform[15] = 1;
- Vector3 bounds = storage->voxel_gi_get_octree_size(base_probe);
+ Vector3 bounds = voxel_gi_get_octree_size(base_probe);
gipd.bounds[0] = bounds.x;
gipd.bounds[1] = bounds.y;
gipd.bounds[2] = bounds.z;
- gipd.dynamic_range = storage->voxel_gi_get_dynamic_range(base_probe) * storage->voxel_gi_get_energy(base_probe);
- gipd.bias = storage->voxel_gi_get_bias(base_probe);
- gipd.normal_bias = storage->voxel_gi_get_normal_bias(base_probe);
- gipd.blend_ambient = !storage->voxel_gi_is_interior(base_probe);
+ gipd.dynamic_range = voxel_gi_get_dynamic_range(base_probe) * voxel_gi_get_energy(base_probe);
+ gipd.bias = voxel_gi_get_bias(base_probe);
+ gipd.normal_bias = voxel_gi_get_normal_bias(base_probe);
+ gipd.blend_ambient = !voxel_gi_is_interior(base_probe);
gipd.mipmaps = gipi->mipmaps.size();
}
@@ -3108,17 +3533,19 @@ void RendererSceneGIRD::setup_voxel_gi_instances(RID p_render_buffers, const Tra
texture = texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE);
}
- if (texture != rb->gi.voxel_gi_textures[i]) {
+ if (texture != rb->rbgi.voxel_gi_textures[i]) {
voxel_gi_instances_changed = true;
- rb->gi.voxel_gi_textures[i] = texture;
+ rb->rbgi.voxel_gi_textures[i] = texture;
}
}
if (voxel_gi_instances_changed) {
- if (RD::get_singleton()->uniform_set_is_valid(rb->gi.uniform_set)) {
- RD::get_singleton()->free(rb->gi.uniform_set);
+ for (uint32_t v = 0; v < RendererSceneRender::MAX_RENDER_VIEWS; v++) {
+ if (RD::get_singleton()->uniform_set_is_valid(rb->rbgi.uniform_set[v])) {
+ RD::get_singleton()->free(rb->rbgi.uniform_set[v]);
+ }
+ rb->rbgi.uniform_set[v] = RID();
}
- rb->gi.uniform_set = RID();
if (rb->volumetric_fog) {
if (RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->fog_uniform_set)) {
RD::get_singleton()->free(rb->volumetric_fog->fog_uniform_set);
@@ -3140,21 +3567,78 @@ void RendererSceneGIRD::setup_voxel_gi_instances(RID p_render_buffers, const Tra
}
}
-void RendererSceneGIRD::process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_voxel_gi_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, RendererSceneRenderRD *p_scene_render) {
+void GI::RenderBuffersGI::free() {
+ for (uint32_t v = 0; v < RendererSceneRender::MAX_RENDER_VIEWS; v++) {
+ if (RD::get_singleton()->uniform_set_is_valid(uniform_set[v])) {
+ RD::get_singleton()->free(uniform_set[v]);
+ }
+ uniform_set[v] = RID();
+ }
+
+ if (scene_data_ubo.is_valid()) {
+ RD::get_singleton()->free(scene_data_ubo);
+ scene_data_ubo = RID();
+ }
+
+ if (ambient_buffer.is_valid()) {
+ if (view_count == 1) {
+ // Only one view? then these are copies of our main buffers.
+ ambient_view[0] = RID();
+ reflection_view[0] = RID();
+ } else {
+ // Multiple views? free our slices.
+ for (uint32_t v = 0; v < view_count; v++) {
+ RD::get_singleton()->free(ambient_view[v]);
+ RD::get_singleton()->free(reflection_view[v]);
+ ambient_view[v] = RID();
+ reflection_view[v] = RID();
+ }
+ }
+
+ // Now we can free our buffers.
+ RD::get_singleton()->free(ambient_buffer);
+ RD::get_singleton()->free(reflection_buffer);
+ ambient_buffer = RID();
+ reflection_buffer = RID();
+ view_count = 0;
+ }
+
+ if (voxel_gi_buffer.is_valid()) {
+ RD::get_singleton()->free(voxel_gi_buffer);
+ voxel_gi_buffer = RID();
+ }
+}
+
+void GI::process_gi(RID p_render_buffers, RID *p_normal_roughness_views, RID p_voxel_gi_buffer, RID p_environment, uint32_t p_view_count, const CameraMatrix *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_cam_transform, const PagedArray<RID> &p_voxel_gi_instances, RendererSceneRenderRD *p_scene_render) {
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton();
+ ERR_FAIL_COND_MSG(p_view_count > 2, "Maximum of 2 views supported for Processing GI.");
+
RD::get_singleton()->draw_command_begin_label("GI Render");
RendererSceneRenderRD::RenderBuffers *rb = p_scene_render->render_buffers_owner.get_or_null(p_render_buffers);
ERR_FAIL_COND(rb == nullptr);
- if (rb->ambient_buffer.is_null() || rb->gi.using_half_size_gi != half_resolution) {
- if (rb->ambient_buffer.is_valid()) {
- RD::get_singleton()->free(rb->ambient_buffer);
- RD::get_singleton()->free(rb->reflection_buffer);
+ if (rb->rbgi.ambient_buffer.is_null() || rb->rbgi.using_half_size_gi != half_resolution || rb->rbgi.view_count != p_view_count) {
+ // Free our old buffer if applicable
+ if (rb->rbgi.ambient_buffer.is_valid()) {
+ if (rb->rbgi.view_count > 1) {
+ for (uint32_t v = 0; v < rb->rbgi.view_count; v++) {
+ RD::get_singleton()->free(rb->rbgi.ambient_view[v]);
+ RD::get_singleton()->free(rb->rbgi.reflection_view[v]);
+ }
+ }
+ RD::get_singleton()->free(rb->rbgi.ambient_buffer);
+ RD::get_singleton()->free(rb->rbgi.reflection_buffer);
}
+ print_line("Allocating GI buffers"); // TESTING REMOVE BEFORE MERGING
+
+ // Remember the view count we're using
+ rb->rbgi.view_count = p_view_count;
+
+ // Create textures for our ambient and reflection data
RD::TextureFormat tf;
tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
tf.width = rb->internal_width;
@@ -3163,222 +3647,280 @@ void RendererSceneGIRD::process_gi(RID p_render_buffers, RID p_normal_roughness_
tf.width >>= 1;
tf.height >>= 1;
}
+ if (p_view_count > 1) {
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ tf.array_layers = p_view_count;
+ } else {
+ tf.texture_type = RD::TEXTURE_TYPE_2D;
+ tf.array_layers = 1;
+ }
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
- rb->reflection_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
- rb->ambient_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
- rb->gi.using_half_size_gi = half_resolution;
- }
-
- PushConstant push_constant;
-
- push_constant.screen_size[0] = rb->internal_width;
- push_constant.screen_size[1] = rb->internal_height;
- push_constant.z_near = p_projection.get_z_near();
- push_constant.z_far = p_projection.get_z_far();
- push_constant.orthogonal = p_projection.is_orthogonal();
- push_constant.proj_info[0] = -2.0f / (rb->internal_width * p_projection.matrix[0][0]);
- push_constant.proj_info[1] = -2.0f / (rb->internal_height * p_projection.matrix[1][1]);
- push_constant.proj_info[2] = (1.0f - p_projection.matrix[0][2]) / p_projection.matrix[0][0];
- push_constant.proj_info[3] = (1.0f + p_projection.matrix[1][2]) / p_projection.matrix[1][1];
- push_constant.max_voxel_gi_instances = MIN((uint64_t)MAX_VOXEL_GI_INSTANCES, p_voxel_gi_instances.size());
- push_constant.high_quality_vct = voxel_gi_quality == RS::VOXEL_GI_QUALITY_HIGH;
-
- bool use_sdfgi = rb->sdfgi != nullptr;
- bool use_voxel_gi_instances = push_constant.max_voxel_gi_instances > 0;
-
- push_constant.cam_rotation[0] = p_transform.basis[0][0];
- push_constant.cam_rotation[1] = p_transform.basis[1][0];
- push_constant.cam_rotation[2] = p_transform.basis[2][0];
- push_constant.cam_rotation[3] = 0;
- push_constant.cam_rotation[4] = p_transform.basis[0][1];
- push_constant.cam_rotation[5] = p_transform.basis[1][1];
- push_constant.cam_rotation[6] = p_transform.basis[2][1];
- push_constant.cam_rotation[7] = 0;
- push_constant.cam_rotation[8] = p_transform.basis[0][2];
- push_constant.cam_rotation[9] = p_transform.basis[1][2];
- push_constant.cam_rotation[10] = p_transform.basis[2][2];
- push_constant.cam_rotation[11] = 0;
-
- if (rb->gi.uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->gi.uniform_set)) {
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.binding = 1;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
- if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
- u.append_id(rb->sdfgi->cascades[j].sdf_tex);
- } else {
- u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
- }
+ rb->rbgi.ambient_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ rb->rbgi.reflection_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ rb->rbgi.using_half_size_gi = half_resolution;
+
+ if (p_view_count == 1) {
+ // Just one view? Copy our buffers
+ rb->rbgi.ambient_view[0] = rb->rbgi.ambient_buffer;
+ rb->rbgi.reflection_view[0] = rb->rbgi.reflection_buffer;
+ } else {
+ // More then one view? Create slices for each view
+ for (uint32_t v = 0; v < p_view_count; v++) {
+ rb->rbgi.ambient_view[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->rbgi.ambient_buffer, v, 0);
+ rb->rbgi.reflection_view[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->rbgi.reflection_buffer, v, 0);
}
- uniforms.push_back(u);
}
- {
- RD::Uniform u;
- u.binding = 2;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
- if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
- u.append_id(rb->sdfgi->cascades[j].light_tex);
- } else {
- u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+
+ // Setup our scene data
+ {
+ SceneData scene_data;
+
+ if (rb->rbgi.scene_data_ubo.is_null()) {
+ rb->rbgi.scene_data_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SceneData));
+ }
+
+ for (uint32_t v = 0; v < p_view_count; v++) {
+ RendererStorageRD::store_camera(p_projections[v].inverse(), scene_data.inv_projection[v]);
+ scene_data.eye_offset[v][0] = p_eye_offsets[v].x;
+ scene_data.eye_offset[v][1] = p_eye_offsets[v].y;
+ scene_data.eye_offset[v][2] = p_eye_offsets[v].z;
+ scene_data.eye_offset[v][3] = 0.0;
+ }
+
+ // Note that we will be ignoring the origin of this transform.
+ RendererStorageRD::store_transform(p_cam_transform, scene_data.cam_transform);
+
+ scene_data.screen_size[0] = rb->internal_width;
+ scene_data.screen_size[1] = rb->internal_height;
+
+ RD::get_singleton()->buffer_update(rb->rbgi.scene_data_ubo, 0, sizeof(SceneData), &scene_data, RD::BARRIER_MASK_COMPUTE);
+ }
+
+ // Now compute the contents of our buffers.
+ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(true);
+
+ for (uint32_t v = 0; v < p_view_count; v++) {
+ // Render each eye seperately.
+ // We need to look into whether we can make our compute shader use Multiview but not sure that works or makes a difference..
+
+ // setup our push constant
+
+ PushConstant push_constant;
+
+ push_constant.view_index = v;
+ push_constant.orthogonal = p_projections[v].is_orthogonal();
+ push_constant.max_voxel_gi_instances = MIN((uint64_t)MAX_VOXEL_GI_INSTANCES, p_voxel_gi_instances.size());
+ push_constant.high_quality_vct = voxel_gi_quality == RS::VOXEL_GI_QUALITY_HIGH;
+
+ push_constant.z_near = p_projections[v].get_z_near();
+ push_constant.z_far = p_projections[v].get_z_far();
+
+ push_constant.proj_info[0] = -2.0f / (rb->internal_width * p_projections[v].matrix[0][0]);
+ push_constant.proj_info[1] = -2.0f / (rb->internal_height * p_projections[v].matrix[1][1]);
+ push_constant.proj_info[2] = (1.0f - p_projections[v].matrix[0][2]) / p_projections[v].matrix[0][0];
+ push_constant.proj_info[3] = (1.0f + p_projections[v].matrix[1][2]) / p_projections[v].matrix[1][1];
+
+ bool use_sdfgi = rb->sdfgi != nullptr;
+ bool use_voxel_gi_instances = push_constant.max_voxel_gi_instances > 0;
+
+ // setup our uniform set
+ if (rb->rbgi.uniform_set[v].is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->rbgi.uniform_set[v])) {
+ Vector<RD::Uniform> uniforms;
+ {
+ RD::Uniform u;
+ u.binding = 1;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
+ if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
+ u.append_id(rb->sdfgi->cascades[j].sdf_tex);
+ } else {
+ u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
}
+ uniforms.push_back(u);
}
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 3;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
- if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
- u.append_id(rb->sdfgi->cascades[j].light_aniso_0_tex);
+ {
+ RD::Uniform u;
+ u.binding = 2;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
+ if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
+ u.append_id(rb->sdfgi->cascades[j].light_tex);
+ } else {
+ u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 3;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
+ if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
+ u.append_id(rb->sdfgi->cascades[j].light_aniso_0_tex);
+ } else {
+ u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.binding = 4;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
+ if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
+ u.append_id(rb->sdfgi->cascades[j].light_aniso_1_tex);
+ } else {
+ u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ }
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 5;
+ if (rb->sdfgi) {
+ u.append_id(rb->sdfgi->occlusion_texture);
} else {
u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
}
+ uniforms.push_back(u);
}
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.binding = 4;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) {
- if (rb->sdfgi && j < rb->sdfgi->cascades.size()) {
- u.append_id(rb->sdfgi->cascades[j].light_aniso_1_tex);
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.binding = 6;
+ u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
+ u.binding = 7;
+ u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 9;
+ u.append_id(rb->rbgi.ambient_view[v]);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
+ u.binding = 10;
+ u.append_id(rb->rbgi.reflection_view[v]);
+ uniforms.push_back(u);
+ }
+
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 11;
+ if (rb->sdfgi) {
+ u.append_id(rb->sdfgi->lightprobe_texture);
} else {
- u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE));
}
+ uniforms.push_back(u);
}
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 5;
- if (rb->sdfgi) {
- u.append_id(rb->sdfgi->occlusion_texture);
- } else {
- u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_3D_WHITE));
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 12;
+ u.append_id(rb->views[v].view_depth);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 13;
+ u.append_id(p_normal_roughness_views[v]);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 14;
+ RID buffer = p_voxel_gi_buffer.is_valid() ? p_voxel_gi_buffer : texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_BLACK);
+ u.append_id(buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 15;
+ u.append_id(sdfgi_ubo);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 16;
+ u.append_id(rb->rbgi.voxel_gi_buffer);
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
+ u.binding = 17;
+ for (int i = 0; i < MAX_VOXEL_GI_INSTANCES; i++) {
+ u.append_id(rb->rbgi.voxel_gi_textures[i]);
+ }
+ uniforms.push_back(u);
+ }
+ {
+ RD::Uniform u;
+ u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
+ u.binding = 18;
+ u.append_id(rb->rbgi.scene_data_ubo);
+ uniforms.push_back(u);
}
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 6;
- u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_SAMPLER;
- u.binding = 7;
- u.append_id(material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED));
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 9;
- u.append_id(rb->ambient_buffer);
- uniforms.push_back(u);
+ rb->rbgi.uniform_set[v] = RD::get_singleton()->uniform_set_create(uniforms, shader.version_get_shader(shader_version, 0), 0);
}
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 10;
- u.append_id(rb->reflection_buffer);
- uniforms.push_back(u);
- }
+ Mode mode;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 11;
- if (rb->sdfgi) {
- u.append_id(rb->sdfgi->lightprobe_texture);
+ if (p_view_count > 1) {
+ if (rb->rbgi.using_half_size_gi) {
+ mode = (use_sdfgi && use_voxel_gi_instances) ? MODE_HALF_RES_COMBINED_MULTIVIEW : (use_sdfgi ? MODE_HALF_RES_SDFGI_MULTIVIEW : MODE_HALF_RES_VOXEL_GI_MULTIVIEW);
} else {
- u.append_id(texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE));
+ mode = (use_sdfgi && use_voxel_gi_instances) ? MODE_COMBINED_MULTIVIEW : (use_sdfgi ? MODE_SDFGI_MULTIVIEW : MODE_VOXEL_GI_MULTIVIEW);
}
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 12;
- u.append_id(rb->depth_texture);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 13;
- u.append_id(p_normal_roughness_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 14;
- RID buffer = p_voxel_gi_buffer.is_valid() ? p_voxel_gi_buffer : texture_storage->texture_rd_get_default(RendererRD::DEFAULT_RD_TEXTURE_BLACK);
- u.append_id(buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.binding = 15;
- u.append_id(sdfgi_ubo);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
- u.binding = 16;
- u.append_id(rb->gi.voxel_gi_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
- u.binding = 17;
- for (int i = 0; i < MAX_VOXEL_GI_INSTANCES; i++) {
- u.append_id(rb->gi.voxel_gi_textures[i]);
+ } else {
+ if (rb->rbgi.using_half_size_gi) {
+ mode = (use_sdfgi && use_voxel_gi_instances) ? MODE_HALF_RES_COMBINED : (use_sdfgi ? MODE_HALF_RES_SDFGI : MODE_HALF_RES_VOXEL_GI);
+ } else {
+ mode = (use_sdfgi && use_voxel_gi_instances) ? MODE_COMBINED : (use_sdfgi ? MODE_SDFGI : MODE_VOXEL_GI);
}
- uniforms.push_back(u);
}
- rb->gi.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, shader.version_get_shader(shader_version, 0), 0);
- }
-
- Mode mode;
+ RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipelines[mode]);
+ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->rbgi.uniform_set[v], 0);
+ RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
- if (rb->gi.using_half_size_gi) {
- mode = (use_sdfgi && use_voxel_gi_instances) ? MODE_HALF_RES_COMBINED : (use_sdfgi ? MODE_HALF_RES_SDFGI : MODE_HALF_RES_VOXEL_GI);
- } else {
- mode = (use_sdfgi && use_voxel_gi_instances) ? MODE_COMBINED : (use_sdfgi ? MODE_SDFGI : MODE_VOXEL_GI);
+ if (rb->rbgi.using_half_size_gi) {
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->internal_width >> 1, rb->internal_height >> 1, 1);
+ } else {
+ RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->internal_width, rb->internal_height, 1);
+ }
}
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(true);
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, pipelines[mode]);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->gi.uniform_set, 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(PushConstant));
-
- if (rb->gi.using_half_size_gi) {
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->internal_width >> 1, rb->internal_height >> 1, 1);
- } else {
- RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->internal_width, rb->internal_height, 1);
- }
//do barrier later to allow oeverlap
//RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER); //no barriers, let other compute, raster and transfer happen at the same time
RD::get_singleton()->draw_command_end_label();
}
-RID RendererSceneGIRD::voxel_gi_instance_create(RID p_base) {
+RID GI::voxel_gi_instance_create(RID p_base) {
VoxelGIInstance voxel_gi;
voxel_gi.gi = this;
voxel_gi.storage = storage;
@@ -3387,28 +3929,28 @@ RID RendererSceneGIRD::voxel_gi_instance_create(RID p_base) {
return rid;
}
-void RendererSceneGIRD::voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform) {
+void GI::voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform) {
VoxelGIInstance *voxel_gi = get_probe_instance(p_probe);
ERR_FAIL_COND(!voxel_gi);
voxel_gi->transform = p_xform;
}
-bool RendererSceneGIRD::voxel_gi_needs_update(RID p_probe) const {
+bool GI::voxel_gi_needs_update(RID p_probe) const {
VoxelGIInstance *voxel_gi = get_probe_instance(p_probe);
ERR_FAIL_COND_V(!voxel_gi, false);
- return voxel_gi->last_probe_version != storage->voxel_gi_get_version(voxel_gi->probe);
+ return voxel_gi->last_probe_version != voxel_gi_get_version(voxel_gi->probe);
}
-void RendererSceneGIRD::voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects, RendererSceneRenderRD *p_scene_render) {
+void GI::voxel_gi_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, const PagedArray<RendererSceneRender::GeometryInstance *> &p_dynamic_objects, RendererSceneRenderRD *p_scene_render) {
VoxelGIInstance *voxel_gi = get_probe_instance(p_probe);
ERR_FAIL_COND(!voxel_gi);
voxel_gi->update(p_update_light_instances, p_light_instances, p_dynamic_objects, p_scene_render);
}
-void RendererSceneGIRD::debug_voxel_gi(RID p_voxel_gi, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha) {
+void GI::debug_voxel_gi(RID p_voxel_gi, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha) {
VoxelGIInstance *voxel_gi = voxel_gi_instance_owner.get_or_null(p_voxel_gi);
ERR_FAIL_COND(!voxel_gi);
diff --git a/servers/rendering/renderer_rd/renderer_scene_gi_rd.h b/servers/rendering/renderer_rd/environment/gi.h
index 122644498b..b6ecfe42ea 100644
--- a/servers/rendering/renderer_rd/renderer_scene_gi_rd.h
+++ b/servers/rendering/renderer_rd/environment/gi.h
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* renderer_scene_gi_rd.h */
+/* gi.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,33 +28,77 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef RENDERING_SERVER_SCENE_GI_RD_H
-#define RENDERING_SERVER_SCENE_GI_RD_H
+#ifndef GI_RD_H
+#define GI_RD_H
#include "core/templates/local_vector.h"
#include "core/templates/rid_owner.h"
+#include "servers/rendering/environment/renderer_gi.h"
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering/renderer_rd/renderer_scene_environment_rd.h"
#include "servers/rendering/renderer_rd/renderer_scene_sky_rd.h"
#include "servers/rendering/renderer_rd/renderer_storage_rd.h"
-#include "servers/rendering/renderer_rd/shaders/gi.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/sdfgi_debug.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/sdfgi_debug_probes.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/sdfgi_direct_light.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/sdfgi_integrate.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/voxel_gi.glsl.gen.h"
-#include "servers/rendering/renderer_rd/shaders/voxel_gi_debug.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/environment/gi.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/environment/sdfgi_preprocess.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/environment/voxel_gi.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/environment/voxel_gi_debug.glsl.gen.h"
#include "servers/rendering/renderer_scene_render.h"
+#include "servers/rendering/renderer_storage.h"
#include "servers/rendering/rendering_device.h"
// Forward declare RenderDataRD and RendererSceneRenderRD so we can pass it into some of our methods, these classes are pretty tightly bound
struct RenderDataRD;
class RendererSceneRenderRD;
-class RendererSceneGIRD {
+namespace RendererRD {
+
+class GI : public RendererGI {
+public:
+ /* VOXEL GI STORAGE */
+
+ struct VoxelGI {
+ RID octree_buffer;
+ RID data_buffer;
+ RID sdf_texture;
+
+ uint32_t octree_buffer_size = 0;
+ uint32_t data_buffer_size = 0;
+
+ Vector<int> level_counts;
+
+ int cell_count = 0;
+
+ Transform3D to_cell_xform;
+ AABB bounds;
+ Vector3i octree_size;
+
+ float dynamic_range = 2.0;
+ float energy = 1.0;
+ float bias = 1.4;
+ float normal_bias = 0.0;
+ float propagation = 0.7;
+ bool interior = false;
+ bool use_two_bounces = false;
+
+ float anisotropy_strength = 0.5;
+
+ uint32_t version = 1;
+ uint32_t data_version = 1;
+
+ RendererStorage::Dependency dependency;
+ };
+
private:
RendererStorageRD *storage = nullptr;
+ static GI *singleton;
+
+ /* VOXEL GI STORAGE */
+
+ mutable RID_Owner<VoxelGI, true> voxel_gi_owner;
/* VOXEL_GI INSTANCE */
@@ -196,10 +240,13 @@ private:
uint32_t use_occlusion;
float y_mult;
- float cam_extent[3];
uint32_t probe_axis_size;
+ float z_near;
+ float reserved1;
+ float reserved2;
float cam_transform[16];
+ float inv_projection[16];
};
SdfgiDebugShaderRD debug;
@@ -209,13 +256,17 @@ private:
enum ProbeDebugMode {
PROBE_DEBUG_PROBES,
+ PROBE_DEBUG_PROBES_MULTIVIEW,
PROBE_DEBUG_VISIBILITY,
+ PROBE_DEBUG_VISIBILITY_MULTIVIEW,
PROBE_DEBUG_MAX
};
- struct DebugProbesPushConstant {
- float projection[16];
+ struct DebugProbesSceneData {
+ float projection[2][16];
+ };
+ struct DebugProbesPushConstant {
uint32_t band_power;
uint32_t sections_in_band;
uint32_t band_mask;
@@ -324,6 +375,60 @@ private:
} sdfgi_shader;
public:
+ static GI *get_singleton() { return singleton; }
+
+ /* VOXEL GI API */
+
+ VoxelGI *get_voxel_gi(RID p_rid) { return voxel_gi_owner.get_or_null(p_rid); };
+ bool owns_voxel_gi(RID p_rid) { return voxel_gi_owner.owns(p_rid); };
+
+ virtual RID voxel_gi_allocate() override;
+ virtual void voxel_gi_free(RID p_voxel_gi) override;
+ virtual void voxel_gi_initialize(RID p_voxel_gi) override;
+
+ virtual void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) override;
+
+ virtual AABB voxel_gi_get_bounds(RID p_voxel_gi) const override;
+ virtual Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const override;
+ virtual Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const override;
+ virtual Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const override;
+ virtual Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const override;
+
+ virtual Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const override;
+ virtual Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) override;
+ virtual float voxel_gi_get_dynamic_range(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) override;
+ virtual float voxel_gi_get_propagation(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_energy) override;
+ virtual float voxel_gi_get_energy(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_bias) override;
+ virtual float voxel_gi_get_bias(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) override;
+ virtual float voxel_gi_get_normal_bias(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) override;
+ virtual bool voxel_gi_is_interior(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) override;
+ virtual bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const override;
+
+ virtual void voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) override;
+ virtual float voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const override;
+
+ virtual uint32_t voxel_gi_get_version(RID p_probe) const override;
+ uint32_t voxel_gi_get_data_version(RID p_probe);
+
+ RID voxel_gi_get_octree_buffer(RID p_voxel_gi) const;
+ RID voxel_gi_get_data_buffer(RID p_voxel_gi) const;
+
+ RID voxel_gi_get_sdf_texture(RID p_voxel_gi);
+
/* VOXEL_GI INSTANCE */
//@TODO VoxelGIInstance is still directly used in the render code, we'll address this when we refactor the render code itself.
@@ -331,7 +436,7 @@ public:
struct VoxelGIInstance {
// access to our containers
RendererStorageRD *storage = nullptr;
- RendererSceneGIRD *gi = nullptr;
+ GI *gi = nullptr;
RID probe;
RID texture;
@@ -455,7 +560,7 @@ public:
// access to our containers
RendererStorageRD *storage = nullptr;
- RendererSceneGIRD *gi = nullptr;
+ GI *gi = nullptr;
// used for rendering (voxelization)
RID render_albedo;
@@ -497,7 +602,8 @@ public:
float min_cell_size = 0;
uint32_t probe_axis_count = 0; //amount of probes per axis, this is an odd number because it encloses endpoints
- RID debug_uniform_set;
+ RID debug_uniform_set[RendererSceneRender::MAX_RENDER_VIEWS];
+ RID debug_probes_scene_data_ubo;
RID debug_probes_uniform_set;
RID cascades_ubo;
@@ -516,7 +622,7 @@ public:
int32_t cascade_dynamic_light_count[SDFGI::MAX_CASCADES]; //used dynamically
RID integrate_sky_uniform_set;
- void create(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, RendererSceneGIRD *p_gi);
+ void create(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size, GI *p_gi);
void erase();
void update(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position);
void update_light();
@@ -525,8 +631,8 @@ public:
int get_pending_region_data(int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds) const;
void update_cascades();
- void debug_draw(const CameraMatrix &p_projection, const Transform3D &p_transform, int p_width, int p_height, RID p_render_target, RID p_texture);
- void debug_probes(RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform);
+ void debug_draw(uint32_t p_view_count, const CameraMatrix *p_projections, const Transform3D &p_transform, int p_width, int p_height, RID p_render_target, RID p_texture, const Vector<RID> &p_texture_views);
+ void debug_probes(RID p_framebuffer, const uint32_t p_view_count, const CameraMatrix *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth);
void pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_render_data, RendererSceneRenderRD *p_scene_render);
void render_region(RID p_render_buffers, int p_region, const PagedArray<RendererSceneRender::GeometryInstance *> &p_instances, RendererSceneRenderRD *p_scene_render);
@@ -561,8 +667,18 @@ public:
RID full_dispatch;
RID full_mask;
- RID uniform_set;
+ /* GI buffers */
+ RID ambient_buffer;
+ RID reflection_buffer;
+ RID ambient_view[RendererSceneRender::MAX_RENDER_VIEWS];
+ RID reflection_view[RendererSceneRender::MAX_RENDER_VIEWS];
+ RID uniform_set[RendererSceneRender::MAX_RENDER_VIEWS];
bool using_half_size_gi = false;
+ uint32_t view_count = 1;
+
+ RID scene_data_ubo;
+
+ void free();
};
struct SDFGIData {
@@ -611,19 +727,28 @@ public:
uint32_t mipmaps; // 4 - 96
};
- struct PushConstant {
- int32_t screen_size[2];
- float z_near;
- float z_far;
+ struct SceneData {
+ float inv_projection[2][16];
+ float cam_transform[16];
+ float eye_offset[2][4];
- float proj_info[4];
+ int32_t screen_size[2];
+ float pad1;
+ float pad2;
+ };
+ struct PushConstant {
+ uint32_t view_index;
uint32_t max_voxel_gi_instances;
uint32_t high_quality_vct;
uint32_t orthogonal;
- uint32_t pad;
- float cam_rotation[12];
+ float proj_info[4];
+
+ float z_near;
+ float z_far;
+ float pad1;
+ float pad2;
};
RID sdfgi_ubo;
@@ -634,6 +759,14 @@ public:
MODE_HALF_RES_VOXEL_GI,
MODE_HALF_RES_SDFGI,
MODE_HALF_RES_COMBINED,
+
+ MODE_VOXEL_GI_MULTIVIEW,
+ MODE_SDFGI_MULTIVIEW,
+ MODE_COMBINED_MULTIVIEW,
+ MODE_HALF_RES_VOXEL_GI_MULTIVIEW,
+ MODE_HALF_RES_SDFGI_MULTIVIEW,
+ MODE_HALF_RES_COMBINED_MULTIVIEW,
+
MODE_MAX
};
@@ -644,8 +777,8 @@ public:
RID shader_version;
RID pipelines[MODE_MAX];
- RendererSceneGIRD();
- ~RendererSceneGIRD();
+ GI();
+ ~GI();
void init(RendererStorageRD *p_storage, RendererSceneSkyRD *p_sky);
void free();
@@ -653,7 +786,7 @@ public:
SDFGI *create_sdfgi(RendererSceneEnvironmentRD *p_env, const Vector3 &p_world_position, uint32_t p_requested_history_size);
void setup_voxel_gi_instances(RID p_render_buffers, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, uint32_t &r_voxel_gi_instances_used, RendererSceneRenderRD *p_scene_render);
- void process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_voxel_gi_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform3D &p_transform, const PagedArray<RID> &p_voxel_gi_instances, RendererSceneRenderRD *p_scene_render);
+ void process_gi(RID p_render_buffers, RID *p_normal_roughness_views, RID p_voxel_gi_buffer, RID p_environment, uint32_t p_view_count, const CameraMatrix *p_projections, const Vector3 *p_eye_offsets, const Transform3D &p_cam_transform, const PagedArray<RID> &p_voxel_gi_instances, RendererSceneRenderRD *p_scene_render);
RID voxel_gi_instance_create(RID p_base);
void voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform);
@@ -662,4 +795,6 @@ public:
void debug_voxel_gi(RID p_voxel_gi, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha);
};
-#endif /* !RENDERING_SERVER_SCENE_GI_RD_H */
+} // namespace RendererRD
+
+#endif /* !GI_RD_H */
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index d390614e53..c7048289c8 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -48,6 +48,13 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_specular()
if (!specular.is_valid()) {
RD::TextureFormat tf;
tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+ if (view_count > 1) {
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ tf.array_layers = view_count;
+ } else {
+ tf.texture_type = RD::TEXTURE_TYPE_2D;
+ tf.array_layers = 1;
+ }
tf.width = width;
tf.height = height;
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
@@ -64,7 +71,7 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_specular()
Vector<RID> fb;
fb.push_back(specular);
- specular_only_fb = RD::get_singleton()->framebuffer_create(fb);
+ specular_only_fb = RD::get_singleton()->framebuffer_create(fb, RD::INVALID_ID, view_count);
}
} else {
@@ -76,7 +83,7 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_specular()
Vector<RID> fb;
fb.push_back(specular_msaa);
- specular_only_fb = RD::get_singleton()->framebuffer_create(fb);
+ specular_only_fb = RD::get_singleton()->framebuffer_create(fb, RD::INVALID_ID, view_count);
}
}
}
@@ -106,6 +113,13 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_velocity()
void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_voxelgi() {
if (!voxelgi_buffer.is_valid()) {
RD::TextureFormat tf;
+ if (view_count > 1) {
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ tf.array_layers = view_count;
+ } else {
+ tf.texture_type = RD::TEXTURE_TYPE_2D;
+ tf.array_layers = 1;
+ }
tf.format = RD::DATA_FORMAT_R8G8_UINT;
tf.width = width;
tf.height = height;
@@ -116,6 +130,14 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_voxelgi()
tf_aa.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
tf_aa.samples = texture_samples;
voxelgi_buffer_msaa = RD::get_singleton()->texture_create(tf_aa, RD::TextureView());
+
+ if (view_count == 1) {
+ voxelgi_msaa_views[0] = voxelgi_buffer_msaa;
+ } else {
+ for (uint32_t v = 0; v < view_count; v++) {
+ voxelgi_msaa_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), voxelgi_buffer_msaa, v, 0);
+ }
+ }
} else {
tf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
}
@@ -124,6 +146,14 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_voxelgi()
voxelgi_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ if (view_count == 1) {
+ voxelgi_views[0] = voxelgi_buffer;
+ } else {
+ for (uint32_t v = 0; v < view_count; v++) {
+ voxelgi_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), voxelgi_buffer, v, 0);
+ }
+ }
+
Vector<RID> fb;
if (msaa != RS::VIEWPORT_MSAA_DISABLED) {
fb.push_back(depth_msaa);
@@ -135,7 +165,7 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::ensure_voxelgi()
fb.push_back(voxelgi_buffer);
}
- depth_normal_roughness_voxelgi_fb = RD::get_singleton()->framebuffer_create(fb);
+ depth_normal_roughness_voxelgi_fb = RD::get_singleton()->framebuffer_create(fb, RD::INVALID_ID, view_count);
}
}
@@ -144,7 +174,25 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::clear() {
RD::get_singleton()->free(voxelgi_buffer);
voxelgi_buffer = RID();
+ if (view_count == 1) {
+ voxelgi_views[0] = RID();
+ } else {
+ for (uint32_t v = 0; v < view_count; v++) {
+ RD::get_singleton()->free(voxelgi_views[v]);
+ voxelgi_views[v] = RID();
+ }
+ }
+
if (voxelgi_buffer_msaa.is_valid()) {
+ if (view_count == 1) {
+ voxelgi_msaa_views[0] = RID();
+ } else {
+ for (uint32_t v = 0; v < view_count; v++) {
+ RD::get_singleton()->free(voxelgi_msaa_views[v]);
+ voxelgi_msaa_views[v] = RID();
+ }
+ }
+
RD::get_singleton()->free(voxelgi_buffer_msaa);
voxelgi_buffer_msaa = RID();
}
@@ -153,11 +201,35 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::clear() {
}
if (color_msaa.is_valid()) {
+ if (view_count == 1) {
+ color_views[0] = RID();
+ color_msaa_views[0] = RID();
+ } else {
+ for (uint32_t v = 0; v < view_count; v++) {
+ RD::get_singleton()->free(color_views[v]);
+ RD::get_singleton()->free(color_msaa_views[v]);
+ color_views[v] = RID();
+ color_msaa_views[v] = RID();
+ }
+ }
+
RD::get_singleton()->free(color_msaa);
color_msaa = RID();
}
if (depth_msaa.is_valid()) {
+ if (view_count == 1) {
+ depth_views[0] = RID();
+ depth_msaa_views[0] = RID();
+ } else {
+ for (uint32_t v = 0; v < view_count; v++) {
+ RD::get_singleton()->free(depth_views[v]);
+ RD::get_singleton()->free(depth_msaa_views[v]);
+ depth_views[v] = RID();
+ depth_msaa_views[v] = RID();
+ }
+ }
+
RD::get_singleton()->free(depth_msaa);
depth_msaa = RID();
}
@@ -178,12 +250,31 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::clear() {
color_framebuffers.clear(); // Color pass framebuffers are freed automatically by their dependency relations
if (normal_roughness_buffer.is_valid()) {
+ if (view_count == 1) {
+ normal_roughness_views[0] = RID();
+ } else {
+ for (uint32_t v = 0; v < view_count; v++) {
+ RD::get_singleton()->free(normal_roughness_views[v]);
+ normal_roughness_views[v] = RID();
+ }
+ }
+
RD::get_singleton()->free(normal_roughness_buffer);
+ normal_roughness_buffer = RID();
+
if (normal_roughness_buffer_msaa.is_valid()) {
+ if (view_count == 1) {
+ normal_roughness_msaa_views[0] = RID();
+ } else {
+ for (uint32_t v = 0; v < view_count; v++) {
+ RD::get_singleton()->free(normal_roughness_msaa_views[v]);
+ normal_roughness_msaa_views[v] = RID();
+ }
+ }
RD::get_singleton()->free(normal_roughness_buffer_msaa);
normal_roughness_buffer_msaa = RID();
}
- normal_roughness_buffer = RID();
+
depth_normal_roughness_fb = RID();
}
@@ -259,6 +350,22 @@ void RenderForwardClustered::RenderBufferDataForwardClustered::configure(RID p_c
depth_msaa = RD::get_singleton()->texture_create(tf, RD::TextureView());
+ if (view_count == 1) {
+ // just reuse
+ color_views[0] = color;
+ depth_views[0] = depth;
+ color_msaa_views[0] = color_msaa;
+ depth_msaa_views[0] = depth_msaa;
+ } else {
+ // create slices
+ for (uint32_t v = 0; v < view_count; v++) {
+ color_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), color, v, 0);
+ depth_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), depth, v, 0);
+ color_msaa_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), color_msaa, v, 0);
+ depth_msaa_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), depth_msaa, v, 0);
+ }
+ }
+
{
Vector<RID> fb;
fb.push_back(color_msaa);
@@ -308,6 +415,8 @@ RID RenderForwardClustered::RenderBufferDataForwardClustered::get_color_pass_fb(
}
void RenderForwardClustered::_allocate_normal_roughness_texture(RenderBufferDataForwardClustered *rb) {
+ ERR_FAIL_COND_MSG(rb->view_count > 2, "Only support up to two views for roughness texture");
+
if (rb->normal_roughness_buffer.is_valid()) {
return;
}
@@ -316,6 +425,13 @@ void RenderForwardClustered::_allocate_normal_roughness_texture(RenderBufferData
tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
tf.width = rb->width;
tf.height = rb->height;
+ if (rb->view_count > 1) {
+ tf.texture_type = RD::TEXTURE_TYPE_2D_ARRAY;
+ tf.array_layers = rb->view_count;
+ } else {
+ tf.texture_type = RD::TEXTURE_TYPE_2D;
+ tf.array_layers = 1;
+ }
tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) {
@@ -330,7 +446,7 @@ void RenderForwardClustered::_allocate_normal_roughness_texture(RenderBufferData
Vector<RID> fb;
fb.push_back(rb->depth);
fb.push_back(rb->normal_roughness_buffer);
- rb->depth_normal_roughness_fb = RD::get_singleton()->framebuffer_create(fb);
+ rb->depth_normal_roughness_fb = RD::get_singleton()->framebuffer_create(fb, RD::INVALID_ID, rb->view_count);
} else {
tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
tf.samples = rb->texture_samples;
@@ -339,7 +455,21 @@ void RenderForwardClustered::_allocate_normal_roughness_texture(RenderBufferData
Vector<RID> fb;
fb.push_back(rb->depth_msaa);
fb.push_back(rb->normal_roughness_buffer_msaa);
- rb->depth_normal_roughness_fb = RD::get_singleton()->framebuffer_create(fb);
+ rb->depth_normal_roughness_fb = RD::get_singleton()->framebuffer_create(fb, RD::INVALID_ID, rb->view_count);
+ }
+
+ if (rb->view_count == 1) {
+ rb->normal_roughness_views[0] = rb->normal_roughness_buffer;
+ if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) {
+ rb->normal_roughness_msaa_views[0] = rb->normal_roughness_buffer_msaa;
+ }
+ } else {
+ for (uint32_t v = 0; v < rb->view_count; v++) {
+ rb->normal_roughness_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->normal_roughness_buffer, v, 0);
+ if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) {
+ rb->normal_roughness_msaa_views[v] = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->normal_roughness_buffer_msaa, v, 0);
+ }
+ }
}
}
@@ -503,22 +633,21 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p
pipeline_version = p_params->view_count > 1 ? SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_MULTIVIEW : SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS;
} break;
case PASS_MODE_SHADOW_DP: {
- ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for shadow DP pass");
+ ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for shadow DP pass");
pipeline_version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_DP;
} break;
case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: {
- ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for depth/roughness pass");
- pipeline_version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS;
+ pipeline_version = p_params->view_count > 1 ? SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW : SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS;
} break;
case PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI: {
- ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for voxel GI pass");
- pipeline_version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI;
+ pipeline_version = p_params->view_count > 1 ? SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW : SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI;
} break;
case PASS_MODE_DEPTH_MATERIAL: {
ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for material pass");
pipeline_version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_MATERIAL;
} break;
case PASS_MODE_SDF: {
+ // Note, SDF is prepared in world space, this shouldn't be a multiview buffer even when stereoscopic rendering is used.
ERR_FAIL_COND_MSG(p_params->view_count > 1, "Multiview not supported for SDF pass");
pipeline_version = SceneShaderForwardClustered::PIPELINE_VERSION_DEPTH_PASS_WITH_SDF;
} break;
@@ -1323,9 +1452,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
using_voxelgi = true;
}
- if (p_render_data->view_count > 1) {
- depth_pass_mode = PASS_MODE_DEPTH;
- } else if (!p_render_data->environment.is_valid() && using_voxelgi) {
+ if (!p_render_data->environment.is_valid() && using_voxelgi) {
depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI;
} else if (p_render_data->environment.is_valid() && (environment_is_ssr_enabled(p_render_data->environment) || environment_is_sdfgi_enabled(p_render_data->environment) || using_voxelgi)) {
if (environment_is_sdfgi_enabled(p_render_data->environment)) {
@@ -1531,9 +1658,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
if (needs_pre_resolve) {
RD::get_singleton()->barrier(RD::BARRIER_MASK_RASTER, RD::BARRIER_MASK_COMPUTE);
}
- storage->get_effects()->resolve_gi(render_buffer->depth_msaa, render_buffer->normal_roughness_buffer_msaa, using_voxelgi ? render_buffer->voxelgi_buffer_msaa : RID(), render_buffer->depth, render_buffer->normal_roughness_buffer, using_voxelgi ? render_buffer->voxelgi_buffer : RID(), Vector2i(render_buffer->width, render_buffer->height), texture_multisamples[render_buffer->msaa]);
+ for (uint32_t v = 0; v < render_buffer->view_count; v++) {
+ resolve_effects->resolve_gi(render_buffer->depth_msaa_views[v], render_buffer->normal_roughness_msaa_views[v], using_voxelgi ? render_buffer->voxelgi_msaa_views[v] : RID(), render_buffer->depth_views[v], render_buffer->normal_roughness_views[v], using_voxelgi ? render_buffer->voxelgi_views[v] : RID(), Vector2i(render_buffer->width, render_buffer->height), texture_multisamples[render_buffer->msaa]);
+ }
} else if (finish_depth) {
- storage->get_effects()->resolve_depth(render_buffer->depth_msaa, render_buffer->depth, Vector2i(render_buffer->width, render_buffer->height), texture_multisamples[render_buffer->msaa]);
+ for (uint32_t v = 0; v < render_buffer->view_count; v++) {
+ resolve_effects->resolve_depth(render_buffer->depth_msaa_views[v], render_buffer->depth_views[v], Vector2i(render_buffer->width, render_buffer->height), texture_multisamples[render_buffer->msaa]);
+ }
}
RD::get_singleton()->draw_command_end_label();
}
@@ -1541,7 +1672,8 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
continue_depth = !finish_depth;
}
- _pre_opaque_render(p_render_data, using_ssao, using_ssil, using_sdfgi || using_voxelgi, render_buffer ? render_buffer->normal_roughness_buffer : RID(), render_buffer ? render_buffer->voxelgi_buffer : RID());
+ RID null_rids[2];
+ _pre_opaque_render(p_render_data, using_ssao, using_ssil, using_sdfgi || using_voxelgi, render_buffer ? render_buffer->normal_roughness_views : null_rids, render_buffer ? render_buffer->voxelgi_buffer : RID());
RD::get_singleton()->draw_command_begin_label("Render Opaque Pass");
@@ -1604,18 +1736,17 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
}
if (debug_sdfgi_probes) {
- //debug voxelgis
+ //debug sdfgi
bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only);
bool will_continue_depth = (can_continue_depth || draw_sky || draw_sky_fog_only);
CameraMatrix dc;
dc.set_depth_correction(true);
- CameraMatrix cm = (dc * p_render_data->cam_projection) * CameraMatrix(p_render_data->cam_transform.affine_inverse());
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::INITIAL_ACTION_CONTINUE, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ);
- RD::get_singleton()->draw_command_begin_label("Debug SDFGI");
- _debug_sdfgi_probes(p_render_data->render_buffers, draw_list, color_only_framebuffer, cm);
- RD::get_singleton()->draw_command_end_label();
- RD::get_singleton()->draw_list_end();
+ CameraMatrix cms[RendererSceneRender::MAX_RENDER_VIEWS];
+ for (uint32_t v = 0; v < p_render_data->view_count; v++) {
+ cms[v] = (dc * p_render_data->view_projection[v]) * CameraMatrix(p_render_data->cam_transform.affine_inverse());
+ }
+ _debug_sdfgi_probes(p_render_data->render_buffers, color_only_framebuffer, p_render_data->view_count, cms, will_continue_color, will_continue_depth);
}
if (draw_sky || draw_sky_fog_only) {
@@ -1635,14 +1766,20 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
}
if (render_buffer && !can_continue_color && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) {
- RD::get_singleton()->texture_resolve_multisample(render_buffer->color_msaa, render_buffer->color);
+ // Handle views individual, might want to look at rewriting our resolve to do both layers in one pass.
+ for (uint32_t v = 0; v < render_buffer->view_count; v++) {
+ RD::get_singleton()->texture_resolve_multisample(render_buffer->color_msaa_views[v], render_buffer->color_views[v]);
+ }
+ // TODO mame this do multiview
if (using_separate_specular) {
RD::get_singleton()->texture_resolve_multisample(render_buffer->specular_msaa, render_buffer->specular);
}
}
if (render_buffer && !can_continue_depth && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) {
- storage->get_effects()->resolve_depth(render_buffer->depth_msaa, render_buffer->depth, Vector2i(render_buffer->width, render_buffer->height), texture_multisamples[render_buffer->msaa]);
+ for (uint32_t v = 0; v < render_buffer->view_count; v++) {
+ resolve_effects->resolve_depth(render_buffer->depth_msaa_views[v], render_buffer->depth_views[v], Vector2i(render_buffer->width, render_buffer->height), texture_multisamples[render_buffer->msaa]);
+ }
}
if (using_separate_specular) {
@@ -1697,11 +1834,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
RD::get_singleton()->draw_command_begin_label("Resolve");
if (render_buffer && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) {
- RD::get_singleton()->texture_resolve_multisample(render_buffer->color_msaa, render_buffer->color);
- if (render_buffer->use_taa) {
+ for (uint32_t v = 0; v < render_buffer->view_count; v++) {
+ RD::get_singleton()->texture_resolve_multisample(render_buffer->color_msaa_views[v], render_buffer->color_views[v]);
+ resolve_effects->resolve_depth(render_buffer->depth_msaa_views[v], render_buffer->depth_views[v], Vector2i(render_buffer->width, render_buffer->height), texture_multisamples[render_buffer->msaa]);
+ }
+ if (render_buffer->use_taa) { // TODO make TAA stereo capable, this will need to be handled in a separate PR
RD::get_singleton()->texture_resolve_multisample(render_buffer->velocity_buffer_msaa, render_buffer->velocity_buffer);
}
- storage->get_effects()->resolve_depth(render_buffer->depth_msaa, render_buffer->depth, Vector2i(render_buffer->width, render_buffer->height), texture_multisamples[render_buffer->msaa]);
}
RD::get_singleton()->draw_command_end_label();
@@ -3320,9 +3459,16 @@ RenderForwardClustered::RenderForwardClustered(RendererStorageRD *p_storage) :
render_list_thread_threshold = GLOBAL_GET("rendering/limits/forward_renderer/threaded_render_minimum_instances");
_update_shader_quality_settings();
+
+ resolve_effects = memnew(RendererRD::Resolve());
}
RenderForwardClustered::~RenderForwardClustered() {
+ if (resolve_effects != nullptr) {
+ memdelete(resolve_effects);
+ resolve_effects = nullptr;
+ }
+
directional_shadow_atlas_set_size(0);
{
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
index 97f39164a4..dd3d14f0a8 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
@@ -32,6 +32,7 @@
#define RENDERING_SERVER_SCENE_RENDER_FORWARD_CLUSTERED_H
#include "core/templates/paged_allocator.h"
+#include "servers/rendering/renderer_rd/effects/resolve.h"
#include "servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h"
#include "servers/rendering/renderer_rd/pipeline_cache_rd.h"
#include "servers/rendering/renderer_rd/renderer_scene_render_rd.h"
@@ -72,7 +73,6 @@ class RenderForwardClustered : public RendererSceneRenderRD {
RENDER_LIST_ALPHA, //used for transparent objects
RENDER_LIST_SECONDARY, //used for shadows and other objects
RENDER_LIST_MAX
-
};
/* Scene Shader */
@@ -99,7 +99,6 @@ class RenderForwardClustered : public RendererSceneRenderRD {
RID depth_msaa;
RID specular_msaa;
RID normal_roughness_buffer_msaa;
- RID roughness_buffer_msaa;
RID voxelgi_buffer_msaa;
RID velocity_buffer_msaa;
@@ -110,7 +109,17 @@ class RenderForwardClustered : public RendererSceneRenderRD {
RID specular_only_fb;
int width, height;
HashMap<uint32_t, RID> color_framebuffers;
+
+ // for multiview
uint32_t view_count;
+ RID color_views[RendererSceneRender::MAX_RENDER_VIEWS]; // we should rewrite this so we get access to the existing views in our renderer, something we can address when we reorg this
+ RID depth_views[RendererSceneRender::MAX_RENDER_VIEWS]; // we should rewrite this so we get access to the existing views in our renderer, something we can address when we reorg this
+ RID color_msaa_views[RendererSceneRender::MAX_RENDER_VIEWS];
+ RID depth_msaa_views[RendererSceneRender::MAX_RENDER_VIEWS];
+ RID normal_roughness_views[RendererSceneRender::MAX_RENDER_VIEWS];
+ RID normal_roughness_msaa_views[RendererSceneRender::MAX_RENDER_VIEWS];
+ RID voxelgi_views[RendererSceneRender::MAX_RENDER_VIEWS];
+ RID voxelgi_msaa_views[RendererSceneRender::MAX_RENDER_VIEWS];
RID render_sdfgi_uniform_set;
void ensure_specular();
@@ -619,6 +628,8 @@ class RenderForwardClustered : public RendererSceneRenderRD {
virtual void _update_shader_quality_settings() override;
+ RendererRD::Resolve *resolve_effects = nullptr;
+
protected:
virtual void _render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) override;
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index cfb30ef2f3..aada989bcb 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -282,6 +282,8 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL,
SHADER_VERSION_DEPTH_PASS_WITH_SDF,
SHADER_VERSION_DEPTH_PASS_MULTIVIEW,
+ SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW,
+ SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW,
SHADER_VERSION_COLOR_PASS,
};
@@ -349,9 +351,9 @@ void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) {
if (k == PIPELINE_VERSION_DEPTH_PASS || k == PIPELINE_VERSION_DEPTH_PASS_DP || k == PIPELINE_VERSION_DEPTH_PASS_MULTIVIEW) {
//none, leave empty
- } else if (k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS) {
+ } else if (k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS || k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW) {
blend_state = blend_state_depth_normal_roughness;
- } else if (k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI) {
+ } else if (k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI || k == PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW) {
blend_state = blend_state_depth_normal_roughness_giprobe;
} else if (k == PIPELINE_VERSION_DEPTH_PASS_WITH_MATERIAL) {
blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way
@@ -527,10 +529,12 @@ void SceneShaderForwardClustered::init(RendererStorageRD *p_storage, const Strin
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_DEPTH_PASS
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); // SHADER_VERSION_DEPTH_PASS_DP
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS
- shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_GIPROBE
+ shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); // SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL
shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n"); // SHADER_VERSION_DEPTH_PASS_WITH_SDF
shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n"); // SHADER_VERSION_DEPTH_PASS_MULTIVIEW
+ shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW
+ shader_versions.push_back("\n#define USE_MULTIVIEW\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_VOXEL_GI\n"); // SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW
Vector<String> color_pass_flags = {
"\n#define MODE_SEPARATE_SPECULAR\n", // SHADER_COLOR_PASS_FLAG_SEPARATE_SPECULAR
@@ -553,6 +557,8 @@ void SceneShaderForwardClustered::init(RendererStorageRD *p_storage, const Strin
if (!RendererCompositorRD::singleton->is_xr_enabled()) {
shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_MULTIVIEW, false);
+ shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW, false);
+ shader.set_variant_enabled(SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW, false);
// TODO Add a way to enable/disable color pass flags
}
}
@@ -621,7 +627,7 @@ void SceneShaderForwardClustered::init(RendererStorageRD *p_storage, const Strin
//builtins
- actions.renames["TIME"] = "scene_data_block.data.time";
+ actions.renames["TIME"] = "global_time";
actions.renames["PI"] = _MKSTR(Math_PI);
actions.renames["TAU"] = _MKSTR(Math_TAU);
actions.renames["E"] = _MKSTR(Math_E);
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
index 79ccf10090..ffa3893b6a 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
@@ -52,6 +52,8 @@ public:
SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL,
SHADER_VERSION_DEPTH_PASS_WITH_SDF,
SHADER_VERSION_DEPTH_PASS_MULTIVIEW,
+ SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW,
+ SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW,
SHADER_VERSION_COLOR_PASS,
SHADER_VERSION_MAX
};
@@ -72,6 +74,8 @@ public:
PIPELINE_VERSION_DEPTH_PASS_WITH_MATERIAL,
PIPELINE_VERSION_DEPTH_PASS_WITH_SDF,
PIPELINE_VERSION_DEPTH_PASS_MULTIVIEW,
+ PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_MULTIVIEW,
+ PIPELINE_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_VOXEL_GI_MULTIVIEW,
PIPELINE_VERSION_COLOR_PASS,
PIPELINE_VERSION_MAX
};
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index eae5685dd1..25acd2e25f 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -675,7 +675,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
RD::get_singleton()->draw_command_end_label(); // Setup Sky resolution buffers
}
- _pre_opaque_render(p_render_data, false, false, false, RID(), RID());
+ RID null_rids[2];
+ _pre_opaque_render(p_render_data, false, false, false, null_rids, RID());
uint32_t spec_constant_base_flags = 0;
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h
index aefc189f68..12bcfc4684 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.h
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h
@@ -98,11 +98,15 @@ protected:
static uint64_t frame;
public:
- RendererLightStorage *get_light_storage() { return light_storage; };
- RendererMaterialStorage *get_material_storage() { return material_storage; };
- RendererMeshStorage *get_mesh_storage() { return mesh_storage; };
- RendererParticlesStorage *get_particles_storage() { return particles_storage; };
- RendererTextureStorage *get_texture_storage() { return texture_storage; };
+ RendererLightStorage *get_light_storage() { return light_storage; }
+ RendererMaterialStorage *get_material_storage() { return material_storage; }
+ RendererMeshStorage *get_mesh_storage() { return mesh_storage; }
+ RendererParticlesStorage *get_particles_storage() { return particles_storage; }
+ RendererTextureStorage *get_texture_storage() { return texture_storage; }
+ RendererGI *get_gi() {
+ ERR_FAIL_NULL_V(scene, nullptr);
+ return scene->get_gi();
+ }
RendererStorage *get_storage() { return storage; }
RendererCanvasRender *get_canvas() { return canvas; }
RendererSceneRender *get_scene() { return scene; }
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index a50a05d905..6f16c0972e 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -74,7 +74,7 @@ void RendererSceneRenderRD::sdfgi_update(RID p_render_buffers, RID p_environment
rb->sdfgi = nullptr;
}
- RendererSceneGIRD::SDFGI *sdfgi = rb->sdfgi;
+ RendererRD::GI::SDFGI *sdfgi = rb->sdfgi;
if (sdfgi == nullptr) {
// re-create
rb->sdfgi = gi.create_sdfgi(env, p_world_position, requested_history_size);
@@ -95,9 +95,9 @@ int RendererSceneRenderRD::sdfgi_get_pending_region_count(RID p_render_buffers)
int dirty_count = 0;
for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) {
- const RendererSceneGIRD::SDFGI::Cascade &c = rb->sdfgi->cascades[i];
+ const RendererRD::GI::SDFGI::Cascade &c = rb->sdfgi->cascades[i];
- if (c.dirty_regions == RendererSceneGIRD::SDFGI::Cascade::DIRTY_ALL) {
+ if (c.dirty_regions == RendererRD::GI::SDFGI::Cascade::DIRTY_ALL) {
dirty_count++;
} else {
for (int j = 0; j < 3; j++) {
@@ -1533,7 +1533,7 @@ void RendererSceneRenderRD::voxel_gi_update(RID p_probe, bool p_update_light_ins
gi.voxel_gi_update(p_probe, p_update_light_instances, p_light_instances, p_dynamic_objects, this);
}
-void RendererSceneRenderRD::_debug_sdfgi_probes(RID p_render_buffers, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform) {
+void RendererSceneRenderRD::_debug_sdfgi_probes(RID p_render_buffers, RID p_framebuffer, const uint32_t p_view_count, const CameraMatrix *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth) {
RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers);
ERR_FAIL_COND(!rb);
@@ -1541,7 +1541,7 @@ void RendererSceneRenderRD::_debug_sdfgi_probes(RID p_render_buffers, RD::DrawLi
return; //nothing to debug
}
- rb->sdfgi->debug_probes(p_draw_list, p_framebuffer, p_camera_with_transform);
+ rb->sdfgi->debug_probes(p_framebuffer, p_view_count, p_camera_with_transforms, p_will_continue_color, p_will_continue_depth);
}
////////////////////////////////
@@ -1950,17 +1950,7 @@ void RendererSceneRenderRD::_free_render_buffer_data(RenderBuffers *rb) {
rb->taa.prev_velocity = RID();
}
- if (rb->ambient_buffer.is_valid()) {
- RD::get_singleton()->free(rb->ambient_buffer);
- RD::get_singleton()->free(rb->reflection_buffer);
- rb->ambient_buffer = RID();
- rb->reflection_buffer = RID();
- }
-
- if (rb->gi.voxel_gi_buffer.is_valid()) {
- RD::get_singleton()->free(rb->gi.voxel_gi_buffer);
- rb->gi.voxel_gi_buffer = RID();
- }
+ rb->rbgi.free();
}
void RendererSceneRenderRD::_process_sss(RID p_render_buffers, const CameraMatrix &p_camera) {
@@ -2796,11 +2786,11 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(RID p_render_buffers, RID
copy_effects->copy_to_fb_rect(_render_buffers_get_normal_texture(p_render_buffers), texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false);
}
- if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_GI_BUFFER && rb->ambient_buffer.is_valid()) {
+ if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_GI_BUFFER && rb->rbgi.ambient_buffer.is_valid()) {
Size2 rtsize = texture_storage->render_target_get_size(rb->render_target);
- RID ambient_texture = rb->ambient_buffer;
- RID reflection_texture = rb->reflection_buffer;
- copy_effects->copy_to_fb_rect(ambient_texture, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture);
+ RID ambient_texture = rb->rbgi.ambient_buffer;
+ RID reflection_texture = rb->rbgi.reflection_buffer;
+ copy_effects->copy_to_fb_rect(ambient_texture, texture_storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture, rb->view_count > 1);
}
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_OCCLUDERS) {
@@ -2869,10 +2859,10 @@ RID RendererSceneRenderRD::render_buffers_get_ssil_texture(RID p_render_buffers)
RID RendererSceneRenderRD::render_buffers_get_voxel_gi_buffer(RID p_render_buffers) {
RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers);
ERR_FAIL_COND_V(!rb, RID());
- if (rb->gi.voxel_gi_buffer.is_null()) {
- rb->gi.voxel_gi_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(RendererSceneGIRD::VoxelGIData) * RendererSceneGIRD::MAX_VOXEL_GI_INSTANCES);
+ if (rb->rbgi.voxel_gi_buffer.is_null()) {
+ rb->rbgi.voxel_gi_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(RendererRD::GI::VoxelGIData) * RendererRD::GI::MAX_VOXEL_GI_INSTANCES);
}
- return rb->gi.voxel_gi_buffer;
+ return rb->rbgi.voxel_gi_buffer;
}
RID RendererSceneRenderRD::render_buffers_get_default_voxel_gi_buffer() {
@@ -2882,12 +2872,13 @@ RID RendererSceneRenderRD::render_buffers_get_default_voxel_gi_buffer() {
RID RendererSceneRenderRD::render_buffers_get_gi_ambient_texture(RID p_render_buffers) {
RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers);
ERR_FAIL_COND_V(!rb, RID());
- return rb->ambient_buffer;
+
+ return rb->rbgi.ambient_buffer;
}
RID RendererSceneRenderRD::render_buffers_get_gi_reflection_texture(RID p_render_buffers) {
RenderBuffers *rb = render_buffers_owner.get_or_null(p_render_buffers);
ERR_FAIL_COND_V(!rb, RID());
- return rb->reflection_buffer;
+ return rb->rbgi.reflection_buffer;
}
uint32_t RendererSceneRenderRD::render_buffers_get_sdfgi_cascade_count(RID p_render_buffers) const {
@@ -2925,7 +2916,7 @@ Vector3i RendererSceneRenderRD::render_buffers_get_sdfgi_cascade_probe_offset(RI
ERR_FAIL_COND_V(!rb, Vector3i());
ERR_FAIL_COND_V(!rb->sdfgi, Vector3i());
ERR_FAIL_UNSIGNED_INDEX_V(p_cascade, rb->sdfgi->cascades.size(), Vector3i());
- int32_t probe_divisor = rb->sdfgi->cascade_size / RendererSceneGIRD::SDFGI::PROBE_DIVISOR;
+ int32_t probe_divisor = rb->sdfgi->cascade_size / RendererRD::GI::SDFGI::PROBE_DIVISOR;
return rb->sdfgi->cascades[p_cascade].position / probe_divisor;
}
@@ -4615,8 +4606,8 @@ void RendererSceneRenderRD::_update_volumetric_fog(RID p_render_buffers, RID p_e
RD::Uniform u;
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
u.binding = 12;
- for (int i = 0; i < RendererSceneGIRD::MAX_VOXEL_GI_INSTANCES; i++) {
- u.append_id(rb->gi.voxel_gi_textures[i]);
+ for (int i = 0; i < RendererRD::GI::MAX_VOXEL_GI_INSTANCES; i++) {
+ u.append_id(rb->rbgi.voxel_gi_textures[i]);
}
uniforms.push_back(u);
copy_uniforms.push_back(u);
@@ -4930,7 +4921,7 @@ void RendererSceneRenderRD::_pre_resolve_render(RenderDataRD *p_render_data, boo
}
}
-void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_gi, RID p_normal_roughness_buffer, RID p_voxel_gi_buffer) {
+void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_gi, RID *p_normal_roughness_views, RID p_voxel_gi_buffer) {
// Render shadows while GI is rendering, due to how barriers are handled, this should happen at the same time
RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton();
@@ -5005,7 +4996,7 @@ void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool
//start GI
if (render_gi) {
- gi.process_gi(p_render_data->render_buffers, p_normal_roughness_buffer, p_voxel_gi_buffer, p_render_data->environment, p_render_data->cam_projection, p_render_data->cam_transform, *p_render_data->voxel_gi_instances, this);
+ gi.process_gi(p_render_data->render_buffers, p_normal_roughness_views, p_voxel_gi_buffer, p_render_data->environment, p_render_data->view_count, p_render_data->view_projection, p_render_data->view_eye_offset, p_render_data->cam_transform, *p_render_data->voxel_gi_instances, this);
}
//Do shadow rendering (in parallel with GI)
@@ -5046,11 +5037,13 @@ void RendererSceneRenderRD::_pre_opaque_render(RenderDataRD *p_render_data, bool
}
if (p_use_ssao) {
- _process_ssao(p_render_data->render_buffers, p_render_data->environment, p_normal_roughness_buffer, p_render_data->cam_projection);
+ // TODO make these proper stereo and thus use p_normal_roughness_views correctly
+ _process_ssao(p_render_data->render_buffers, p_render_data->environment, p_normal_roughness_views[0], p_render_data->cam_projection);
}
if (p_use_ssil) {
- _process_ssil(p_render_data->render_buffers, p_render_data->environment, p_normal_roughness_buffer, p_render_data->cam_projection, p_render_data->cam_transform);
+ // TODO make these proper stereo and thus use p_normal_roughness_views correctly
+ _process_ssil(p_render_data->render_buffers, p_render_data->environment, p_normal_roughness_views[0], p_render_data->cam_projection, p_render_data->cam_transform);
}
}
@@ -5195,7 +5188,7 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData
//assign render indices to voxel_gi_instances
if (is_dynamic_gi_supported()) {
for (uint32_t i = 0; i < (uint32_t)p_voxel_gi_instances.size(); i++) {
- RendererSceneGIRD::VoxelGIInstance *voxel_gi_inst = gi.voxel_gi_instance_owner.get_or_null(p_voxel_gi_instances[i]);
+ RendererRD::GI::VoxelGIInstance *voxel_gi_inst = gi.voxel_gi_instance_owner.get_or_null(p_voxel_gi_instances[i]);
if (voxel_gi_inst) {
voxel_gi_inst->render_index = i;
}
@@ -5249,7 +5242,13 @@ void RendererSceneRenderRD::render_scene(RID p_render_buffers, const CameraData
_render_buffers_debug_draw(p_render_buffers, p_shadow_atlas, p_occluder_debug_tex);
if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI && rb != nullptr && rb->sdfgi != nullptr) {
- rb->sdfgi->debug_draw(render_data.cam_projection, render_data.cam_transform, rb->width, rb->height, rb->render_target, rb->texture);
+ Vector<RID> view_rids;
+
+ for (int v = 0; v < rb->views.size(); v++) {
+ view_rids.push_back(rb->views[v].view_texture);
+ }
+
+ rb->sdfgi->debug_draw(render_data.view_count, render_data.view_projection, render_data.cam_transform, rb->width, rb->height, rb->render_target, rb->texture, view_rids);
}
}
}
@@ -5518,7 +5517,7 @@ bool RendererSceneRenderRD::free(RID p_rid) {
} else if (lightmap_instance_owner.owns(p_rid)) {
lightmap_instance_owner.free(p_rid);
} else if (gi.voxel_gi_instance_owner.owns(p_rid)) {
- RendererSceneGIRD::VoxelGIInstance *voxel_gi = gi.voxel_gi_instance_owner.get_or_null(p_rid);
+ RendererRD::GI::VoxelGIInstance *voxel_gi = gi.voxel_gi_instance_owner.get_or_null(p_rid);
if (voxel_gi->texture.is_valid()) {
RD::get_singleton()->free(voxel_gi->texture);
RD::get_singleton()->free(voxel_gi->write_buffer);
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
index a90c165d83..c87fd6703f 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
@@ -38,8 +38,8 @@
#include "servers/rendering/renderer_rd/effects/bokeh_dof.h"
#include "servers/rendering/renderer_rd/effects/copy_effects.h"
#include "servers/rendering/renderer_rd/effects/tone_mapper.h"
+#include "servers/rendering/renderer_rd/environment/gi.h"
#include "servers/rendering/renderer_rd/renderer_scene_environment_rd.h"
-#include "servers/rendering/renderer_rd/renderer_scene_gi_rd.h"
#include "servers/rendering/renderer_rd/renderer_scene_sky_rd.h"
#include "servers/rendering/renderer_rd/renderer_storage_rd.h"
#include "servers/rendering/renderer_rd/shaders/volumetric_fog.glsl.gen.h"
@@ -99,7 +99,7 @@ struct RenderDataRD {
class RendererSceneRenderRD : public RendererSceneRender {
friend RendererSceneSkyRD;
- friend RendererSceneGIRD;
+ friend RendererRD::GI;
protected:
RendererStorageRD *storage = nullptr;
@@ -131,7 +131,7 @@ protected:
virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<GeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) = 0;
virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const CameraMatrix &p_cam_projection, const PagedArray<GeometryInstance *> &p_instances) = 0;
- void _debug_sdfgi_probes(RID p_render_buffers, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform);
+ void _debug_sdfgi_probes(RID p_render_buffers, RID p_framebuffer, uint32_t p_view_count, const CameraMatrix *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth);
void _debug_draw_cluster(RID p_render_buffers);
RenderBufferData *render_buffers_get_data(RID p_render_buffers);
@@ -151,7 +151,7 @@ protected:
void _post_prepass_render(RenderDataRD *p_render_data, bool p_use_gi);
void _pre_resolve_render(RenderDataRD *p_render_data, bool p_use_gi);
- void _pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_gi, RID p_normal_roughness_buffer, RID p_voxel_gi_buffer);
+ void _pre_opaque_render(RenderDataRD *p_render_data, bool p_use_ssao, bool p_use_ssil, bool p_use_gi, RID *p_normal_roughness_views, RID p_voxel_gi_buffer);
void _render_buffers_copy_screen_texture(const RenderDataRD *p_render_data);
void _render_buffers_copy_depth_texture(const RenderDataRD *p_render_data);
@@ -163,7 +163,7 @@ protected:
PagedArrayPool<GeometryInstance *> cull_argument_pool;
PagedArray<GeometryInstance *> cull_argument; //need this to exist
- RendererSceneGIRD gi;
+ RendererRD::GI gi;
RendererSceneSkyRD sky;
RendererSceneEnvironmentRD *get_environment(RID p_environment) {
@@ -503,9 +503,9 @@ private:
};
Vector<View> views;
- RendererSceneGIRD::SDFGI *sdfgi = nullptr;
+ RendererRD::GI::SDFGI *sdfgi = nullptr;
VolumetricFog *volumetric_fog = nullptr;
- RendererSceneGIRD::RenderBuffersGI gi;
+ RendererRD::GI::RenderBuffersGI rbgi;
ClusterBuilderRD *cluster_builder = nullptr;
@@ -606,9 +606,6 @@ private:
RID temp;
RID prev_velocity; // Last frame velocity buffer
} taa;
-
- RID ambient_buffer;
- RID reflection_buffer;
};
/* GI */
@@ -997,6 +994,10 @@ public:
virtual Transform3D geometry_instance_get_transform(GeometryInstance *p_instance) = 0;
virtual AABB geometry_instance_get_aabb(GeometryInstance *p_instance) = 0;
+ /* GI */
+
+ RendererRD::GI *get_gi() { return &gi; }
+
/* SHADOW ATLAS API */
virtual RID shadow_atlas_create() override;
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
index d5166c6905..8c55ff1d0a 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
@@ -35,6 +35,7 @@
#include "core/io/resource_loader.h"
#include "core/math/math_defs.h"
#include "renderer_compositor_rd.h"
+#include "servers/rendering/renderer_rd/environment/gi.h"
#include "servers/rendering/renderer_rd/storage_rd/light_storage.h"
#include "servers/rendering/renderer_rd/storage_rd/mesh_storage.h"
#include "servers/rendering/renderer_rd/storage_rd/particles_storage.h"
@@ -173,336 +174,6 @@ void RendererStorageRD::visibility_notifier_call(RID p_notifier, bool p_enter, b
}
}
-/* VOXEL GI */
-
-RID RendererStorageRD::voxel_gi_allocate() {
- return voxel_gi_owner.allocate_rid();
-}
-void RendererStorageRD::voxel_gi_initialize(RID p_voxel_gi) {
- voxel_gi_owner.initialize_rid(p_voxel_gi, VoxelGI());
-}
-
-void RendererStorageRD::voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND(!voxel_gi);
-
- if (voxel_gi->octree_buffer.is_valid()) {
- RD::get_singleton()->free(voxel_gi->octree_buffer);
- RD::get_singleton()->free(voxel_gi->data_buffer);
- if (voxel_gi->sdf_texture.is_valid()) {
- RD::get_singleton()->free(voxel_gi->sdf_texture);
- }
-
- voxel_gi->sdf_texture = RID();
- voxel_gi->octree_buffer = RID();
- voxel_gi->data_buffer = RID();
- voxel_gi->octree_buffer_size = 0;
- voxel_gi->data_buffer_size = 0;
- voxel_gi->cell_count = 0;
- }
-
- voxel_gi->to_cell_xform = p_to_cell_xform;
- voxel_gi->bounds = p_aabb;
- voxel_gi->octree_size = p_octree_size;
- voxel_gi->level_counts = p_level_counts;
-
- if (p_octree_cells.size()) {
- ERR_FAIL_COND(p_octree_cells.size() % 32 != 0); //cells size must be a multiple of 32
-
- uint32_t cell_count = p_octree_cells.size() / 32;
-
- ERR_FAIL_COND(p_data_cells.size() != (int)cell_count * 16); //see that data size matches
-
- voxel_gi->cell_count = cell_count;
- voxel_gi->octree_buffer = RD::get_singleton()->storage_buffer_create(p_octree_cells.size(), p_octree_cells);
- voxel_gi->octree_buffer_size = p_octree_cells.size();
- voxel_gi->data_buffer = RD::get_singleton()->storage_buffer_create(p_data_cells.size(), p_data_cells);
- voxel_gi->data_buffer_size = p_data_cells.size();
-
- if (p_distance_field.size()) {
- RD::TextureFormat tf;
- tf.format = RD::DATA_FORMAT_R8_UNORM;
- tf.width = voxel_gi->octree_size.x;
- tf.height = voxel_gi->octree_size.y;
- tf.depth = voxel_gi->octree_size.z;
- tf.texture_type = RD::TEXTURE_TYPE_3D;
- tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT;
- Vector<Vector<uint8_t>> s;
- s.push_back(p_distance_field);
- voxel_gi->sdf_texture = RD::get_singleton()->texture_create(tf, RD::TextureView(), s);
- }
-#if 0
- {
- RD::TextureFormat tf;
- tf.format = RD::DATA_FORMAT_R8_UNORM;
- tf.width = voxel_gi->octree_size.x;
- tf.height = voxel_gi->octree_size.y;
- tf.depth = voxel_gi->octree_size.z;
- tf.type = RD::TEXTURE_TYPE_3D;
- tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
- tf.shareable_formats.push_back(RD::DATA_FORMAT_R8_UNORM);
- tf.shareable_formats.push_back(RD::DATA_FORMAT_R8_UINT);
- voxel_gi->sdf_texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
- }
- RID shared_tex;
- {
- RD::TextureView tv;
- tv.format_override = RD::DATA_FORMAT_R8_UINT;
- shared_tex = RD::get_singleton()->texture_create_shared(tv, voxel_gi->sdf_texture);
- }
- //update SDF texture
- Vector<RD::Uniform> uniforms;
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 1;
- u.append_id(voxel_gi->octree_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER;
- u.binding = 2;
- u.append_id(voxel_gi->data_buffer);
- uniforms.push_back(u);
- }
- {
- RD::Uniform u;
- u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
- u.binding = 3;
- u.append_id(shared_tex);
- uniforms.push_back(u);
- }
-
- RID uniform_set = RD::get_singleton()->uniform_set_create(uniforms, voxel_gi_sdf_shader_version_shader, 0);
-
- {
- uint32_t push_constant[4] = { 0, 0, 0, 0 };
-
- for (int i = 0; i < voxel_gi->level_counts.size() - 1; i++) {
- push_constant[0] += voxel_gi->level_counts[i];
- }
- push_constant[1] = push_constant[0] + voxel_gi->level_counts[voxel_gi->level_counts.size() - 1];
-
- print_line("offset: " + itos(push_constant[0]));
- print_line("size: " + itos(push_constant[1]));
- //create SDF
- RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
- RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, voxel_gi_sdf_shader_pipeline);
- RD::get_singleton()->compute_list_bind_uniform_set(compute_list, uniform_set, 0);
- RD::get_singleton()->compute_list_set_push_constant(compute_list, push_constant, sizeof(uint32_t) * 4);
- RD::get_singleton()->compute_list_dispatch(compute_list, voxel_gi->octree_size.x / 4, voxel_gi->octree_size.y / 4, voxel_gi->octree_size.z / 4);
- RD::get_singleton()->compute_list_end();
- }
-
- RD::get_singleton()->free(uniform_set);
- RD::get_singleton()->free(shared_tex);
- }
-#endif
- }
-
- voxel_gi->version++;
- voxel_gi->data_version++;
-
- voxel_gi->dependency.changed_notify(DEPENDENCY_CHANGED_AABB);
-}
-
-AABB RendererStorageRD::voxel_gi_get_bounds(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, AABB());
-
- return voxel_gi->bounds;
-}
-
-Vector3i RendererStorageRD::voxel_gi_get_octree_size(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, Vector3i());
- return voxel_gi->octree_size;
-}
-
-Vector<uint8_t> RendererStorageRD::voxel_gi_get_octree_cells(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, Vector<uint8_t>());
-
- if (voxel_gi->octree_buffer.is_valid()) {
- return RD::get_singleton()->buffer_get_data(voxel_gi->octree_buffer);
- }
- return Vector<uint8_t>();
-}
-
-Vector<uint8_t> RendererStorageRD::voxel_gi_get_data_cells(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, Vector<uint8_t>());
-
- if (voxel_gi->data_buffer.is_valid()) {
- return RD::get_singleton()->buffer_get_data(voxel_gi->data_buffer);
- }
- return Vector<uint8_t>();
-}
-
-Vector<uint8_t> RendererStorageRD::voxel_gi_get_distance_field(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, Vector<uint8_t>());
-
- if (voxel_gi->data_buffer.is_valid()) {
- return RD::get_singleton()->texture_get_data(voxel_gi->sdf_texture, 0);
- }
- return Vector<uint8_t>();
-}
-
-Vector<int> RendererStorageRD::voxel_gi_get_level_counts(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, Vector<int>());
-
- return voxel_gi->level_counts;
-}
-
-Transform3D RendererStorageRD::voxel_gi_get_to_cell_xform(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, Transform3D());
-
- return voxel_gi->to_cell_xform;
-}
-
-void RendererStorageRD::voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND(!voxel_gi);
-
- voxel_gi->dynamic_range = p_range;
- voxel_gi->version++;
-}
-
-float RendererStorageRD::voxel_gi_get_dynamic_range(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, 0);
-
- return voxel_gi->dynamic_range;
-}
-
-void RendererStorageRD::voxel_gi_set_propagation(RID p_voxel_gi, float p_range) {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND(!voxel_gi);
-
- voxel_gi->propagation = p_range;
- voxel_gi->version++;
-}
-
-float RendererStorageRD::voxel_gi_get_propagation(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, 0);
- return voxel_gi->propagation;
-}
-
-void RendererStorageRD::voxel_gi_set_energy(RID p_voxel_gi, float p_energy) {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND(!voxel_gi);
-
- voxel_gi->energy = p_energy;
-}
-
-float RendererStorageRD::voxel_gi_get_energy(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, 0);
- return voxel_gi->energy;
-}
-
-void RendererStorageRD::voxel_gi_set_bias(RID p_voxel_gi, float p_bias) {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND(!voxel_gi);
-
- voxel_gi->bias = p_bias;
-}
-
-float RendererStorageRD::voxel_gi_get_bias(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, 0);
- return voxel_gi->bias;
-}
-
-void RendererStorageRD::voxel_gi_set_normal_bias(RID p_voxel_gi, float p_normal_bias) {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND(!voxel_gi);
-
- voxel_gi->normal_bias = p_normal_bias;
-}
-
-float RendererStorageRD::voxel_gi_get_normal_bias(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, 0);
- return voxel_gi->normal_bias;
-}
-
-void RendererStorageRD::voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND(!voxel_gi);
-
- voxel_gi->anisotropy_strength = p_strength;
-}
-
-float RendererStorageRD::voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, 0);
- return voxel_gi->anisotropy_strength;
-}
-
-void RendererStorageRD::voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND(!voxel_gi);
-
- voxel_gi->interior = p_enable;
-}
-
-void RendererStorageRD::voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND(!voxel_gi);
-
- voxel_gi->use_two_bounces = p_enable;
- voxel_gi->version++;
-}
-
-bool RendererStorageRD::voxel_gi_is_using_two_bounces(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, false);
- return voxel_gi->use_two_bounces;
-}
-
-bool RendererStorageRD::voxel_gi_is_interior(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, 0);
- return voxel_gi->interior;
-}
-
-uint32_t RendererStorageRD::voxel_gi_get_version(RID p_voxel_gi) {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, 0);
- return voxel_gi->version;
-}
-
-uint32_t RendererStorageRD::voxel_gi_get_data_version(RID p_voxel_gi) {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, 0);
- return voxel_gi->data_version;
-}
-
-RID RendererStorageRD::voxel_gi_get_octree_buffer(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, RID());
- return voxel_gi->octree_buffer;
-}
-
-RID RendererStorageRD::voxel_gi_get_data_buffer(RID p_voxel_gi) const {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, RID());
- return voxel_gi->data_buffer;
-}
-
-RID RendererStorageRD::voxel_gi_get_sdf_texture(RID p_voxel_gi) {
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_COND_V(!voxel_gi, RID());
-
- return voxel_gi->sdf_texture;
-}
-
/* misc */
void RendererStorageRD::base_update_dependency(RID p_base, DependencyTracker *p_instance) {
@@ -521,8 +192,8 @@ void RendererStorageRD::base_update_dependency(RID p_base, DependencyTracker *p_
} else if (RendererRD::TextureStorage::get_singleton()->owns_decal(p_base)) {
RendererRD::Decal *decal = RendererRD::TextureStorage::get_singleton()->get_decal(p_base);
p_instance->update_dependency(&decal->dependency);
- } else if (voxel_gi_owner.owns(p_base)) {
- VoxelGI *gip = voxel_gi_owner.get_or_null(p_base);
+ } else if (RendererRD::GI::get_singleton()->owns_voxel_gi(p_base)) {
+ RendererRD::GI::VoxelGI *gip = RendererRD::GI::get_singleton()->get_voxel_gi(p_base);
p_instance->update_dependency(&gip->dependency);
} else if (RendererRD::LightStorage::get_singleton()->owns_lightmap(p_base)) {
RendererRD::Lightmap *lm = RendererRD::LightStorage::get_singleton()->get_lightmap(p_base);
@@ -558,7 +229,7 @@ RS::InstanceType RendererStorageRD::get_base_type(RID p_rid) const {
if (RendererRD::TextureStorage::get_singleton()->owns_decal(p_rid)) {
return RS::INSTANCE_DECAL;
}
- if (voxel_gi_owner.owns(p_rid)) {
+ if (RendererRD::GI::get_singleton()->owns_voxel_gi(p_rid)) {
return RS::INSTANCE_VOXEL_GI;
}
if (RendererRD::LightStorage::get_singleton()->owns_light(p_rid)) {
@@ -636,11 +307,8 @@ bool RendererStorageRD::free(RID p_rid) {
RendererRD::LightStorage::get_singleton()->reflection_probe_free(p_rid);
} else if (RendererRD::TextureStorage::get_singleton()->owns_decal(p_rid)) {
RendererRD::TextureStorage::get_singleton()->decal_free(p_rid);
- } else if (voxel_gi_owner.owns(p_rid)) {
- voxel_gi_allocate_data(p_rid, Transform3D(), AABB(), Vector3i(), Vector<uint8_t>(), Vector<uint8_t>(), Vector<uint8_t>(), Vector<int>()); //deallocate
- VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_rid);
- voxel_gi->dependency.deleted_notify(p_rid);
- voxel_gi_owner.free(p_rid);
+ } else if (RendererRD::GI::get_singleton()->owns_voxel_gi(p_rid)) {
+ RendererRD::GI::get_singleton()->voxel_gi_free(p_rid);
} else if (RendererRD::LightStorage::get_singleton()->owns_lightmap(p_rid)) {
RendererRD::LightStorage::get_singleton()->lightmap_free(p_rid);
} else if (RendererRD::LightStorage::get_singleton()->owns_light(p_rid)) {
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.h b/servers/rendering/renderer_rd/renderer_storage_rd.h
index 07fae45a26..d41129d678 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.h
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.h
@@ -36,7 +36,7 @@
#include "core/templates/rid_owner.h"
#include "servers/rendering/renderer_compositor.h"
#include "servers/rendering/renderer_rd/effects_rd.h"
-#include "servers/rendering/renderer_rd/shaders/voxel_gi_sdf.glsl.gen.h"
+#include "servers/rendering/renderer_rd/shaders/environment/voxel_gi_sdf.glsl.gen.h"
#include "servers/rendering/renderer_rd/storage_rd/material_storage.h"
#include "servers/rendering/renderer_scene_render.h"
#include "servers/rendering/rendering_device.h"
@@ -147,42 +147,6 @@ private:
mutable RID_Owner<VisibilityNotifier> visibility_notifier_owner;
- /* VOXEL GI */
-
- struct VoxelGI {
- RID octree_buffer;
- RID data_buffer;
- RID sdf_texture;
-
- uint32_t octree_buffer_size = 0;
- uint32_t data_buffer_size = 0;
-
- Vector<int> level_counts;
-
- int cell_count = 0;
-
- Transform3D to_cell_xform;
- AABB bounds;
- Vector3i octree_size;
-
- float dynamic_range = 2.0;
- float energy = 1.0;
- float bias = 1.4;
- float normal_bias = 0.0;
- float propagation = 0.7;
- bool interior = false;
- bool use_two_bounces = false;
-
- float anisotropy_strength = 0.5;
-
- uint32_t version = 1;
- uint32_t data_version = 1;
-
- Dependency dependency;
- };
-
- mutable RID_Owner<VoxelGI, true> voxel_gi_owner;
-
/* EFFECTS */
EffectsRD *effects = nullptr;
@@ -192,54 +156,6 @@ public:
void base_update_dependency(RID p_base, DependencyTracker *p_instance);
- /* VOXEL GI API */
-
- RID voxel_gi_allocate();
- void voxel_gi_initialize(RID p_voxel_gi);
-
- void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts);
-
- AABB voxel_gi_get_bounds(RID p_voxel_gi) const;
- Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const;
- Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const;
- Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const;
- Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const;
-
- Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const;
- Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const;
-
- void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range);
- float voxel_gi_get_dynamic_range(RID p_voxel_gi) const;
-
- void voxel_gi_set_propagation(RID p_voxel_gi, float p_range);
- float voxel_gi_get_propagation(RID p_voxel_gi) const;
-
- void voxel_gi_set_energy(RID p_voxel_gi, float p_energy);
- float voxel_gi_get_energy(RID p_voxel_gi) const;
-
- void voxel_gi_set_bias(RID p_voxel_gi, float p_bias);
- float voxel_gi_get_bias(RID p_voxel_gi) const;
-
- void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range);
- float voxel_gi_get_normal_bias(RID p_voxel_gi) const;
-
- void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable);
- bool voxel_gi_is_interior(RID p_voxel_gi) const;
-
- void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable);
- bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const;
-
- void voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength);
- float voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const;
-
- uint32_t voxel_gi_get_version(RID p_probe);
- uint32_t voxel_gi_get_data_version(RID p_probe);
-
- RID voxel_gi_get_octree_buffer(RID p_voxel_gi) const;
- RID voxel_gi_get_data_buffer(RID p_voxel_gi) const;
-
- RID voxel_gi_get_sdf_texture(RID p_voxel_gi);
-
/* FOG VOLUMES */
virtual RID fog_volume_allocate();
diff --git a/servers/rendering/renderer_rd/shaders/SCsub b/servers/rendering/renderer_rd/shaders/SCsub
index acb843bfb6..05663226c0 100644
--- a/servers/rendering/renderer_rd/shaders/SCsub
+++ b/servers/rendering/renderer_rd/shaders/SCsub
@@ -17,3 +17,4 @@ if "RD_GLSL" in env["BUILDERS"]:
env.RD_GLSL(glsl_file)
SConscript("effects/SCsub")
+SConscript("environment/SCsub")
diff --git a/servers/rendering/renderer_rd/shaders/resolve.glsl b/servers/rendering/renderer_rd/shaders/effects/resolve.glsl
index 0e086331c0..0e086331c0 100644
--- a/servers/rendering/renderer_rd/shaders/resolve.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/resolve.glsl
diff --git a/servers/rendering/renderer_rd/shaders/environment/SCsub b/servers/rendering/renderer_rd/shaders/environment/SCsub
new file mode 100644
index 0000000000..fc513d3fb9
--- /dev/null
+++ b/servers/rendering/renderer_rd/shaders/environment/SCsub
@@ -0,0 +1,17 @@
+#!/usr/bin/env python
+
+Import("env")
+
+if "RD_GLSL" in env["BUILDERS"]:
+ # find all include files
+ gl_include_files = [str(f) for f in Glob("*_inc.glsl")]
+
+ # find all shader code(all glsl files excluding our include files)
+ glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files]
+
+ # make sure we recompile shaders if include files change
+ env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files)
+
+ # compile shaders
+ for glsl_file in glsl_files:
+ env.RD_GLSL(glsl_file)
diff --git a/servers/rendering/renderer_rd/shaders/gi.glsl b/servers/rendering/renderer_rd/shaders/environment/gi.glsl
index 0c7f08813b..f687d50a2d 100644
--- a/servers/rendering/renderer_rd/shaders/gi.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/gi.glsl
@@ -86,19 +86,29 @@ voxel_gi_instances;
layout(set = 0, binding = 17) uniform texture3D voxel_gi_textures[MAX_VOXEL_GI_INSTANCES];
-layout(push_constant, std430) uniform Params {
- ivec2 screen_size;
- float z_near;
- float z_far;
+layout(set = 0, binding = 18, std140) uniform SceneData {
+ mat4x4 inv_projection[2];
+ mat4x4 cam_transform;
+ vec4 eye_offset[2];
- vec4 proj_info;
+ ivec2 screen_size;
+ float pad1;
+ float pad2;
+}
+scene_data;
+layout(push_constant, std430) uniform Params {
+ uint view_index;
uint max_voxel_gi_instances;
bool high_quality_vct;
bool orthogonal;
- uint pad;
- mat3x4 cam_rotation;
+ vec4 proj_info;
+
+ float z_near;
+ float z_far;
+ float pad1;
+ float pad2;
}
params;
@@ -130,6 +140,16 @@ vec4 blend_color(vec4 src, vec4 dst) {
}
vec3 reconstruct_position(ivec2 screen_pos) {
+#ifdef USE_MULTIVIEW
+ vec4 pos;
+ pos.xy = (2.0 * vec2(screen_pos) / vec2(scene_data.screen_size)) - 1.0;
+ pos.z = texelFetch(sampler2D(depth_buffer, linear_sampler), screen_pos, 0).r * 2.0 - 1.0;
+ pos.w = 1.0;
+
+ pos = scene_data.inv_projection[params.view_index] * pos;
+
+ return pos.xyz / pos.w;
+#else
vec3 pos;
pos.z = texelFetch(sampler2D(depth_buffer, linear_sampler), screen_pos, 0).r;
@@ -147,6 +167,7 @@ vec3 reconstruct_position(ivec2 screen_pos) {
}
return pos;
+#endif
}
void sdfvoxel_gi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal, vec3 cam_specular_normal, float roughness, out vec3 diffuse_light, out vec3 specular_light) {
@@ -579,9 +600,10 @@ void process_gi(ivec2 pos, vec3 vertex, inout vec4 ambient_light, inout vec4 ref
if (normal.length() > 0.5) {
//valid normal, can do GI
float roughness = normal_roughness.w;
- vertex = mat3(params.cam_rotation) * vertex;
- normal = normalize(mat3(params.cam_rotation) * normal);
- vec3 reflection = normalize(reflect(normalize(vertex), normal));
+ vec3 view = -normalize(mat3(scene_data.cam_transform) * (vertex - scene_data.eye_offset[params.view_index].xyz));
+ vertex = mat3(scene_data.cam_transform) * vertex;
+ normal = normalize(mat3(scene_data.cam_transform) * normal);
+ vec3 reflection = normalize(reflect(-view, normal));
#ifdef USE_SDFGI
sdfgi_process(vertex, normal, reflection, roughness, ambient_light, reflection_light);
@@ -629,7 +651,7 @@ void main() {
#ifdef MODE_HALF_RES
pos <<= 1;
#endif
- if (any(greaterThanEqual(pos, params.screen_size))) { //too large, do nothing
+ if (any(greaterThanEqual(pos, scene_data.screen_size))) { //too large, do nothing
return;
}
diff --git a/servers/rendering/renderer_rd/shaders/sdfgi_debug.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl
index 802a410825..af5f7d0a58 100644
--- a/servers/rendering/renderer_rd/shaders/sdfgi_debug.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug.glsl
@@ -40,10 +40,13 @@ layout(push_constant, std430) uniform Params {
bool use_occlusion;
float y_mult;
- vec3 cam_extent;
int probe_axis_size;
+ float z_near;
+ float reserved1;
+ float reserved2;
mat4 cam_transform;
+ mat4 inv_projection;
}
params;
@@ -81,8 +84,9 @@ void main() {
{
ray_pos = params.cam_transform[3].xyz;
- ray_dir.xy = params.cam_extent.xy * ((vec2(screen_pos) / vec2(params.screen_size)) * 2.0 - 1.0);
- ray_dir.z = params.cam_extent.z;
+ ray_dir.xy = ((vec2(screen_pos) / vec2(params.screen_size)) * 2.0 - 1.0);
+ ray_dir.z = params.z_near;
+ ray_dir = (params.inv_projection * vec4(ray_dir, 1.0)).xyz;
ray_dir = normalize(mat3(params.cam_transform) * ray_dir);
}
diff --git a/servers/rendering/renderer_rd/shaders/sdfgi_debug_probes.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl
index e0be0bca12..75b1ad2130 100644
--- a/servers/rendering/renderer_rd/shaders/sdfgi_debug_probes.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_debug_probes.glsl
@@ -2,13 +2,28 @@
#version 450
+#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview)
+#extension GL_EXT_multiview : enable
+#endif
+
+#ifdef USE_MULTIVIEW
+#ifdef has_VK_KHR_multiview
+#define ViewIndex gl_ViewIndex
+#else // has_VK_KHR_multiview
+// !BAS! This needs to become an input once we implement our fallback!
+#define ViewIndex 0
+#endif // has_VK_KHR_multiview
+#else // USE_MULTIVIEW
+// Set to zero, not supported in non stereo
+#define ViewIndex 0
+#endif //USE_MULTIVIEW
+
#VERSION_DEFINES
#define MAX_CASCADES 8
+#define MAX_VIEWS 2
layout(push_constant, std430) uniform Params {
- mat4 projection;
-
uint band_power;
uint sections_in_band;
uint band_mask;
@@ -68,6 +83,11 @@ cascades;
layout(set = 0, binding = 4) uniform texture3D occlusion_texture;
layout(set = 0, binding = 3) uniform sampler linear_sampler;
+layout(set = 0, binding = 5, std140) uniform SceneData {
+ mat4 projection[MAX_VIEWS];
+}
+scene_data;
+
void main() {
#ifdef MODE_PROBES
probe_index = gl_InstanceIndex;
@@ -85,7 +105,7 @@ void main() {
vertex += (cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size) / vec3(1.0, params.y_mult, 1.0);
- gl_Position = params.projection * vec4(vertex, 1.0);
+ gl_Position = scene_data.projection[ViewIndex] * vec4(vertex, 1.0);
#endif
#ifdef MODE_VISIBILITY
@@ -144,7 +164,7 @@ void main() {
visibility = dot(texelFetch(sampler3D(occlusion_texture, linear_sampler), tex_pos, 0), layer_axis[occlusion_layer]);
- gl_Position = params.projection * vec4(vertex, 1.0);
+ gl_Position = scene_data.projection[ViewIndex] * vec4(vertex, 1.0);
#endif
}
@@ -153,16 +173,32 @@ void main() {
#version 450
+#if defined(USE_MULTIVIEW) && defined(has_VK_KHR_multiview)
+#extension GL_EXT_multiview : enable
+#endif
+
+#ifdef USE_MULTIVIEW
+#ifdef has_VK_KHR_multiview
+#define ViewIndex gl_ViewIndex
+#else // has_VK_KHR_multiview
+// !BAS! This needs to become an input once we implement our fallback!
+#define ViewIndex 0
+#endif // has_VK_KHR_multiview
+#else // USE_MULTIVIEW
+// Set to zero, not supported in non stereo
+#define ViewIndex 0
+#endif //USE_MULTIVIEW
+
#VERSION_DEFINES
+#define MAX_VIEWS 2
+
layout(location = 0) out vec4 frag_color;
layout(set = 0, binding = 2) uniform texture2DArray lightprobe_texture;
layout(set = 0, binding = 3) uniform sampler linear_sampler;
layout(push_constant, std430) uniform Params {
- mat4 projection;
-
uint band_power;
uint sections_in_band;
uint band_mask;
diff --git a/servers/rendering/renderer_rd/shaders/sdfgi_direct_light.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl
index b95fad650e..b95fad650e 100644
--- a/servers/rendering/renderer_rd/shaders/sdfgi_direct_light.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_direct_light.glsl
diff --git a/servers/rendering/renderer_rd/shaders/sdfgi_integrate.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl
index 9c03297f5c..9c03297f5c 100644
--- a/servers/rendering/renderer_rd/shaders/sdfgi_integrate.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_integrate.glsl
diff --git a/servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl b/servers/rendering/renderer_rd/shaders/environment/sdfgi_preprocess.glsl
index bce98f4054..bce98f4054 100644
--- a/servers/rendering/renderer_rd/shaders/sdfgi_preprocess.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/sdfgi_preprocess.glsl
diff --git a/servers/rendering/renderer_rd/shaders/voxel_gi.glsl b/servers/rendering/renderer_rd/shaders/environment/voxel_gi.glsl
index 577c6d0cd0..577c6d0cd0 100644
--- a/servers/rendering/renderer_rd/shaders/voxel_gi.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/voxel_gi.glsl
diff --git a/servers/rendering/renderer_rd/shaders/voxel_gi_debug.glsl b/servers/rendering/renderer_rd/shaders/environment/voxel_gi_debug.glsl
index fd7a2bf8ad..fd7a2bf8ad 100644
--- a/servers/rendering/renderer_rd/shaders/voxel_gi_debug.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/voxel_gi_debug.glsl
diff --git a/servers/rendering/renderer_rd/shaders/voxel_gi_sdf.glsl b/servers/rendering/renderer_rd/shaders/environment/voxel_gi_sdf.glsl
index 47a611a543..47a611a543 100644
--- a/servers/rendering/renderer_rd/shaders/voxel_gi_sdf.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/voxel_gi_sdf.glsl
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
index b992e948c3..5947fc5351 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered.glsl
@@ -96,6 +96,8 @@ layout(set = MATERIAL_UNIFORM_SET, binding = 0, std140) uniform MaterialUniforms
} material;
#endif
+float global_time;
+
#ifdef MODE_DUAL_PARABOLOID
layout(location = 9) out float dp_clip;
@@ -386,9 +388,12 @@ void main() {
mat4 model_matrix = instances.data[instance_index].transform;
#if defined(MOTION_VECTORS)
+ global_time = scene_data_block.prev_data.time;
vertex_shader(instance_index, is_multimesh, scene_data_block.prev_data, instances.data[instance_index].prev_transform, prev_screen_position);
+ global_time = scene_data_block.data.time;
vertex_shader(instance_index, is_multimesh, scene_data_block.data, model_matrix, screen_position);
#else
+ global_time = scene_data_block.data.time;
vec4 screen_position;
vertex_shader(instance_index, is_multimesh, scene_data_block.data, model_matrix, screen_position);
#endif
@@ -486,6 +491,8 @@ layout(location = 10) in flat uint instance_index_interp;
#define inv_projection_matrix scene_data.inv_projection_matrix
#endif
+#define global_time scene_data_block.data.time
+
#if defined(ENABLE_SSS) && defined(ENABLE_TRANSMITTANCE)
//both required for transmittance to be enabled
#define LIGHT_TRANSMITTANCE_USED
@@ -1231,12 +1238,20 @@ void fragment_shader(in SceneData scene_data) {
if (scene_data.gi_upscale_for_msaa) {
vec2 base_coord = screen_uv;
vec2 closest_coord = base_coord;
+#ifdef USE_MULTIVIEW
+ float closest_ang = dot(normal, textureLod(sampler2DArray(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(base_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0);
+#else // USE_MULTIVIEW
float closest_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), base_coord, 0.0).xyz * 2.0 - 1.0);
+#endif // USE_MULTIVIEW
for (int i = 0; i < 4; i++) {
const vec2 neighbours[4] = vec2[](vec2(-1, 0), vec2(1, 0), vec2(0, -1), vec2(0, 1));
vec2 neighbour_coord = base_coord + neighbours[i] * scene_data.screen_pixel_size;
+#ifdef USE_MULTIVIEW
+ float neighbour_ang = dot(normal, textureLod(sampler2DArray(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(neighbour_coord, ViewIndex), 0.0).xyz * 2.0 - 1.0);
+#else // USE_MULTIVIEW
float neighbour_ang = dot(normal, textureLod(sampler2D(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), neighbour_coord, 0.0).xyz * 2.0 - 1.0);
+#endif // USE_MULTIVIEW
if (neighbour_ang > closest_ang) {
closest_ang = neighbour_ang;
closest_coord = neighbour_coord;
@@ -1249,8 +1264,13 @@ void fragment_shader(in SceneData scene_data) {
coord = screen_uv;
}
+#ifdef USE_MULTIVIEW
+ vec4 buffer_ambient = textureLod(sampler2DArray(ambient_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(coord, ViewIndex), 0.0);
+ vec4 buffer_reflection = textureLod(sampler2DArray(reflection_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), vec3(coord, ViewIndex), 0.0);
+#else // USE_MULTIVIEW
vec4 buffer_ambient = textureLod(sampler2D(ambient_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), coord, 0.0);
vec4 buffer_reflection = textureLod(sampler2D(reflection_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), coord, 0.0);
+#endif // USE_MULTIVIEW
ambient_light = mix(ambient_light, buffer_ambient.rgb, buffer_ambient.a);
specular_light = mix(specular_light, buffer_reflection.rgb, buffer_reflection.a);
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl
index 32ea83397a..0c23de96c3 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_clustered_inc.glsl
@@ -317,10 +317,16 @@ layout(r32ui, set = 1, binding = 12) uniform restrict uimage3D geom_facing_grid;
layout(set = 1, binding = 9) uniform texture2D depth_buffer;
layout(set = 1, binding = 10) uniform texture2D color_buffer;
+#ifdef USE_MULTIVIEW
+layout(set = 1, binding = 11) uniform texture2DArray normal_roughness_buffer;
+layout(set = 1, binding = 13) uniform texture2DArray ambient_buffer;
+layout(set = 1, binding = 14) uniform texture2DArray reflection_buffer;
+#else // USE_MULTIVIEW
layout(set = 1, binding = 11) uniform texture2D normal_roughness_buffer;
-layout(set = 1, binding = 12) uniform texture2D ao_buffer;
layout(set = 1, binding = 13) uniform texture2D ambient_buffer;
layout(set = 1, binding = 14) uniform texture2D reflection_buffer;
+#endif
+layout(set = 1, binding = 12) uniform texture2D ao_buffer;
layout(set = 1, binding = 15) uniform texture2DArray sdfgi_lightprobe_texture;
layout(set = 1, binding = 16) uniform texture3D sdfgi_occlusion_cascades;
diff --git a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
index cd94908c31..26d0de46c2 100644
--- a/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/scene_forward_mobile.glsl
@@ -114,9 +114,9 @@ invariant gl_Position;
#GLOBALS
-void main() {
- SceneData scene_data = scene_data_block.data;
+#define scene_data scene_data_block.data
+void main() {
vec4 instance_custom = vec4(0.0);
#if defined(COLOR_USED)
color_interp = color_attrib;
@@ -576,13 +576,14 @@ vec4 fog_process(vec3 vertex) {
#endif //!MODE_RENDER DEPTH
+#define scene_data scene_data_block.data
+
void main() {
#ifdef MODE_DUAL_PARABOLOID
if (dp_clip > 0.0)
discard;
#endif
- SceneData scene_data = scene_data_block.data;
//lay out everything, whatever is unused is optimized away anyway
vec3 vertex = vertex_interp;
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 6f427272b5..c97ca3de5e 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -1894,7 +1894,7 @@ void RendererSceneCull::_update_instance_aabb(Instance *p_instance) {
} break;
case RenderingServer::INSTANCE_VOXEL_GI: {
- new_aabb = RSG::storage->voxel_gi_get_bounds(p_instance->base);
+ new_aabb = RSG::gi->voxel_gi_get_bounds(p_instance->base);
} break;
case RenderingServer::INSTANCE_LIGHTMAP: {
diff --git a/servers/rendering/renderer_storage.h b/servers/rendering/renderer_storage.h
index bb4acab582..859950673e 100644
--- a/servers/rendering/renderer_storage.h
+++ b/servers/rendering/renderer_storage.h
@@ -122,48 +122,6 @@ public:
virtual void base_update_dependency(RID p_base, DependencyTracker *p_instance) = 0;
- /* VOXEL GI API */
-
- virtual RID voxel_gi_allocate() = 0;
- virtual void voxel_gi_initialize(RID p_rid) = 0;
-
- virtual void voxel_gi_allocate_data(RID p_voxel_gi, const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3i &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) = 0;
-
- virtual AABB voxel_gi_get_bounds(RID p_voxel_gi) const = 0;
- virtual Vector3i voxel_gi_get_octree_size(RID p_voxel_gi) const = 0;
- virtual Vector<uint8_t> voxel_gi_get_octree_cells(RID p_voxel_gi) const = 0;
- virtual Vector<uint8_t> voxel_gi_get_data_cells(RID p_voxel_gi) const = 0;
- virtual Vector<uint8_t> voxel_gi_get_distance_field(RID p_voxel_gi) const = 0;
-
- virtual Vector<int> voxel_gi_get_level_counts(RID p_voxel_gi) const = 0;
- virtual Transform3D voxel_gi_get_to_cell_xform(RID p_voxel_gi) const = 0;
-
- virtual void voxel_gi_set_dynamic_range(RID p_voxel_gi, float p_range) = 0;
- virtual float voxel_gi_get_dynamic_range(RID p_voxel_gi) const = 0;
-
- virtual void voxel_gi_set_propagation(RID p_voxel_gi, float p_range) = 0;
- virtual float voxel_gi_get_propagation(RID p_voxel_gi) const = 0;
-
- virtual void voxel_gi_set_energy(RID p_voxel_gi, float p_energy) = 0;
- virtual float voxel_gi_get_energy(RID p_voxel_gi) const = 0;
-
- virtual void voxel_gi_set_bias(RID p_voxel_gi, float p_bias) = 0;
- virtual float voxel_gi_get_bias(RID p_voxel_gi) const = 0;
-
- virtual void voxel_gi_set_normal_bias(RID p_voxel_gi, float p_range) = 0;
- virtual float voxel_gi_get_normal_bias(RID p_voxel_gi) const = 0;
-
- virtual void voxel_gi_set_interior(RID p_voxel_gi, bool p_enable) = 0;
- virtual bool voxel_gi_is_interior(RID p_voxel_gi) const = 0;
-
- virtual void voxel_gi_set_use_two_bounces(RID p_voxel_gi, bool p_enable) = 0;
- virtual bool voxel_gi_is_using_two_bounces(RID p_voxel_gi) const = 0;
-
- virtual void voxel_gi_set_anisotropy_strength(RID p_voxel_gi, float p_strength) = 0;
- virtual float voxel_gi_get_anisotropy_strength(RID p_voxel_gi) const = 0;
-
- virtual uint32_t voxel_gi_get_version(RID p_probe) = 0;
-
/* FOG VOLUMES */
virtual RID fog_volume_allocate() = 0;
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 2e0c4e0f79..ab9ee2067f 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -1193,6 +1193,20 @@ void RendererViewport::viewport_set_sdf_oversize_and_scale(RID p_viewport, RS::V
RSG::texture_storage->render_target_set_sdf_size_and_scale(viewport->render_target, p_size, p_scale);
}
+RID RendererViewport::viewport_find_from_screen_attachment(DisplayServer::WindowID p_id) const {
+ RID *rids = nullptr;
+ uint32_t rid_count = viewport_owner.get_rid_count();
+ rids = (RID *)alloca(sizeof(RID *) * rid_count);
+ viewport_owner.fill_owned_buffer(rids);
+ for (uint32_t i = 0; i < rid_count; i++) {
+ Viewport *viewport = viewport_owner.get_or_null(rids[i]);
+ if (viewport->viewport_to_screen == p_id) {
+ return rids[i];
+ }
+ }
+ return RID();
+}
+
bool RendererViewport::free(RID p_rid) {
if (viewport_owner.owns(p_rid)) {
Viewport *viewport = viewport_owner.get_or_null(p_rid);
diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h
index 18385d1613..acab8063d1 100644
--- a/servers/rendering/renderer_viewport.h
+++ b/servers/rendering/renderer_viewport.h
@@ -282,6 +282,8 @@ public:
void viewport_set_sdf_oversize_and_scale(RID p_viewport, RS::ViewportSDFOversize p_over_size, RS::ViewportSDFScale p_scale);
+ virtual RID viewport_find_from_screen_attachment(DisplayServer::WindowID p_id = DisplayServer::MAIN_WINDOW_ID) const;
+
void handle_timestamp(String p_timestamp, uint64_t p_cpu_time, uint64_t p_gpu_time);
void set_default_clear_color(const Color &p_color);
diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp
index 718f20f80a..7e035bae80 100644
--- a/servers/rendering/rendering_server_default.cpp
+++ b/servers/rendering/rendering_server_default.cpp
@@ -333,13 +333,10 @@ void RenderingServerDefault::_thread_exit() {
}
void RenderingServerDefault::_thread_draw(bool p_swap_buffers, double frame_step) {
- if (!draw_pending.decrement()) {
- _draw(p_swap_buffers, frame_step);
- }
+ _draw(p_swap_buffers, frame_step);
}
void RenderingServerDefault::_thread_flush() {
- draw_pending.decrement();
}
void RenderingServerDefault::_thread_callback(void *_instance) {
@@ -370,7 +367,6 @@ void RenderingServerDefault::_thread_loop() {
void RenderingServerDefault::sync() {
if (create_thread) {
- draw_pending.increment();
command_queue.push_and_sync(this, &RenderingServerDefault::_thread_flush);
} else {
command_queue.flush_all(); //flush all pending from other threads
@@ -379,7 +375,6 @@ void RenderingServerDefault::sync() {
void RenderingServerDefault::draw(bool p_swap_buffers, double frame_step) {
if (create_thread) {
- draw_pending.increment();
command_queue.push(this, &RenderingServerDefault::_thread_draw, p_swap_buffers, frame_step);
} else {
_draw(p_swap_buffers, frame_step);
@@ -409,6 +404,7 @@ RenderingServerDefault::RenderingServerDefault(bool p_create_thread) :
RSG::mesh_storage = RSG::rasterizer->get_mesh_storage();
RSG::particles_storage = RSG::rasterizer->get_particles_storage();
RSG::texture_storage = RSG::rasterizer->get_texture_storage();
+ RSG::gi = RSG::rasterizer->get_gi();
RSG::storage = RSG::rasterizer->get_storage();
RSG::canvas_render = RSG::rasterizer->get_canvas();
sr->set_scene_render(RSG::rasterizer->get_scene());
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 6008f2b00e..aef89619db 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -84,7 +84,6 @@ class RenderingServerDefault : public RenderingServer {
SafeFlag draw_thread_up;
bool create_thread;
- SafeNumeric<uint64_t> draw_pending;
void _thread_draw(bool p_swap_buffers, double frame_step);
void _thread_flush();
@@ -436,8 +435,8 @@ public:
#undef ServerName
#undef server_name
-#define ServerName RendererStorage
-#define server_name RSG::storage
+#define ServerName RendererGI
+#define server_name RSG::gi
FUNCRIDSPLIT(voxel_gi)
@@ -629,6 +628,7 @@ public:
FUNC2(viewport_set_measure_render_time, RID, bool)
FUNC1RC(double, viewport_get_measured_render_time_cpu, RID)
FUNC1RC(double, viewport_get_measured_render_time_gpu, RID)
+ FUNC1RC(RID, viewport_find_from_screen_attachment, DisplayServer::WindowID)
FUNC2(call_set_vsync_mode, DisplayServer::VSyncMode, DisplayServer::WindowID)
diff --git a/servers/rendering/rendering_server_globals.cpp b/servers/rendering/rendering_server_globals.cpp
index 4d24dbf3b4..1c2a275265 100644
--- a/servers/rendering/rendering_server_globals.cpp
+++ b/servers/rendering/rendering_server_globals.cpp
@@ -37,6 +37,7 @@ RendererMaterialStorage *RenderingServerGlobals::material_storage = nullptr;
RendererMeshStorage *RenderingServerGlobals::mesh_storage = nullptr;
RendererParticlesStorage *RenderingServerGlobals::particles_storage = nullptr;
RendererTextureStorage *RenderingServerGlobals::texture_storage = nullptr;
+RendererGI *RenderingServerGlobals::gi = nullptr;
RendererStorage *RenderingServerGlobals::storage = nullptr;
RendererCanvasRender *RenderingServerGlobals::canvas_render = nullptr;
RendererCompositor *RenderingServerGlobals::rasterizer = nullptr;
diff --git a/servers/rendering/rendering_server_globals.h b/servers/rendering/rendering_server_globals.h
index ce11fae9e8..91712f2a51 100644
--- a/servers/rendering/rendering_server_globals.h
+++ b/servers/rendering/rendering_server_globals.h
@@ -31,6 +31,7 @@
#ifndef RENDERING_SERVER_GLOBALS_H
#define RENDERING_SERVER_GLOBALS_H
+#include "servers/rendering/environment/renderer_gi.h"
#include "servers/rendering/renderer_canvas_cull.h"
#include "servers/rendering/renderer_canvas_render.h"
#include "servers/rendering/renderer_scene.h"
@@ -53,6 +54,7 @@ public:
static RendererMeshStorage *mesh_storage;
static RendererParticlesStorage *particles_storage;
static RendererTextureStorage *texture_storage;
+ static RendererGI *gi;
static RendererStorage *storage;
static RendererCanvasRender *canvas_render;
static RendererCompositor *rasterizer;
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 39484e532a..ccef95f5f2 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -944,6 +944,8 @@ public:
virtual double viewport_get_measured_render_time_cpu(RID p_viewport) const = 0;
virtual double viewport_get_measured_render_time_gpu(RID p_viewport) const = 0;
+ virtual RID viewport_find_from_screen_attachment(DisplayServer::WindowID p_id = DisplayServer::MAIN_WINDOW_ID) const = 0;
+
/* SKY API */
enum SkyMode {
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 7d5c80f61f..1cc272b1bf 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -254,6 +254,7 @@ Files generated from upstream source:
Files extracted from upstream source:
- `jpgd*.{c,h}`
+- `jpge*.{c,h}`
## libogg
@@ -525,7 +526,7 @@ Patch files are provided in `oidn/patches/`.
## openxr
- Upstream: https://github.com/KhronosGroup/OpenXR-SDK
-- Version: 1.0.22 (458984d7f59d1ae6dc1b597d94b02e4f7132eaba, 2022)
+- Version: 1.0.23 (885a90f8934d84121344ba8e4aa5159d5b496e08, 2022)
- License: Apache 2.0
Files extracted from upstream source:
diff --git a/thirdparty/jpeg-compressor/jpge.cpp b/thirdparty/jpeg-compressor/jpge.cpp
new file mode 100644
index 0000000000..5a36c19653
--- /dev/null
+++ b/thirdparty/jpeg-compressor/jpge.cpp
@@ -0,0 +1,1076 @@
+// jpge.cpp - C++ class for JPEG compression. Richard Geldreich <richgel99@gmail.com>
+// Supports grayscale, H1V1, H2V1, and H2V2 chroma subsampling factors, one or two pass Huffman table optimization, libjpeg-style quality 1-100 quality factors.
+// Also supports using luma quantization tables for chroma.
+//
+// Released under two licenses. You are free to choose which license you want:
+// License 1:
+// Public Domain
+//
+// License 2:
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// v1.01, Dec. 18, 2010 - Initial release
+// v1.02, Apr. 6, 2011 - Removed 2x2 ordered dither in H2V1 chroma subsampling method load_block_16_8_8(). (The rounding factor was 2, when it should have been 1. Either way, it wasn't helping.)
+// v1.03, Apr. 16, 2011 - Added support for optimized Huffman code tables, optimized dynamic memory allocation down to only 1 alloc.
+// Also from Alex Evans: Added RGBA support, linear memory allocator (no longer needed in v1.03).
+// v1.04, May. 19, 2012: Forgot to set m_pFile ptr to NULL in cfile_stream::close(). Thanks to Owen Kaluza for reporting this bug.
+// Code tweaks to fix VS2008 static code analysis warnings (all looked harmless).
+// Code review revealed method load_block_16_8_8() (used for the non-default H2V1 sampling mode to downsample chroma) somehow didn't get the rounding factor fix from v1.02.
+// v1.05, March 25, 2020: Added Apache 2.0 alternate license
+
+#include "jpge.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#define JPGE_MAX(a,b) (((a)>(b))?(a):(b))
+#define JPGE_MIN(a,b) (((a)<(b))?(a):(b))
+
+namespace jpge {
+
+ static inline void* jpge_malloc(size_t nSize) { return malloc(nSize); }
+ static inline void jpge_free(void* p) { free(p); }
+
+ // Various JPEG enums and tables.
+ enum { M_SOF0 = 0xC0, M_DHT = 0xC4, M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_APP0 = 0xE0 };
+ enum { DC_LUM_CODES = 12, AC_LUM_CODES = 256, DC_CHROMA_CODES = 12, AC_CHROMA_CODES = 256, MAX_HUFF_SYMBOLS = 257, MAX_HUFF_CODESIZE = 32 };
+
+ static uint8 s_zag[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 };
+ static int16 s_std_lum_quant[64] = { 16,11,12,14,12,10,16,14,13,14,18,17,16,19,24,40,26,24,22,22,24,49,35,37,29,40,58,51,61,60,57,51,56,55,64,72,92,78,64,68,87,69,55,56,80,109,81,87,95,98,103,104,103,62,77,113,121,112,100,120,92,101,103,99 };
+ static int16 s_std_croma_quant[64] = { 17,18,18,24,21,24,47,26,26,47,99,66,56,66,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99 };
+
+ // Table from http://www.imagemagick.org/discourse-server/viewtopic.php?f=22&t=20333&p=98008#p98008
+ // This is mozjpeg's default table, in zag order.
+ static int16 s_alt_quant[64] = { 16,16,16,16,17,16,18,20,20,18,25,27,24,27,25,37,34,31,31,34,37,56,40,43,40,43,40,56,85,53,62,53,53,62,53,85,75,91,74,69,74,91,75,135,106,94,94,106,135,156,131,124,131,156,189,169,169,189,238,226,238,311,311,418 };
+
+ static uint8 s_dc_lum_bits[17] = { 0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0 };
+ static uint8 s_dc_lum_val[DC_LUM_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
+ static uint8 s_ac_lum_bits[17] = { 0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d };
+ static uint8 s_ac_lum_val[AC_LUM_CODES] =
+ {
+ 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
+ 0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
+ 0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
+ 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
+ 0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
+ 0xf9,0xfa
+ };
+ static uint8 s_dc_chroma_bits[17] = { 0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0 };
+ static uint8 s_dc_chroma_val[DC_CHROMA_CODES] = { 0,1,2,3,4,5,6,7,8,9,10,11 };
+ static uint8 s_ac_chroma_bits[17] = { 0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77 };
+ static uint8 s_ac_chroma_val[AC_CHROMA_CODES] =
+ {
+ 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
+ 0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
+ 0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
+ 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
+ 0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
+ 0xf9,0xfa
+ };
+
+ // Low-level helper functions.
+ template <class T> inline void clear_obj(T& obj) { memset(&obj, 0, sizeof(obj)); }
+
+ const int YR = 19595, YG = 38470, YB = 7471, CB_R = -11059, CB_G = -21709, CB_B = 32768, CR_R = 32768, CR_G = -27439, CR_B = -5329;
+ static inline uint8 clamp(int i) { if (static_cast<uint>(i) > 255U) { if (i < 0) i = 0; else if (i > 255) i = 255; } return static_cast<uint8>(i); }
+
+ static inline int left_shifti(int val, uint32 bits)
+ {
+ return static_cast<int>(static_cast<uint32>(val) << bits);
+ }
+
+ static void RGB_to_YCC(uint8* pDst, const uint8* pSrc, int num_pixels)
+ {
+ for (; num_pixels; pDst += 3, pSrc += 3, num_pixels--)
+ {
+ const int r = pSrc[0], g = pSrc[1], b = pSrc[2];
+ pDst[0] = static_cast<uint8>((r * YR + g * YG + b * YB + 32768) >> 16);
+ pDst[1] = clamp(128 + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16));
+ pDst[2] = clamp(128 + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16));
+ }
+ }
+
+ static void RGB_to_Y(uint8* pDst, const uint8* pSrc, int num_pixels)
+ {
+ for (; num_pixels; pDst++, pSrc += 3, num_pixels--)
+ pDst[0] = static_cast<uint8>((pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) >> 16);
+ }
+
+ static void RGBA_to_YCC(uint8* pDst, const uint8* pSrc, int num_pixels)
+ {
+ for (; num_pixels; pDst += 3, pSrc += 4, num_pixels--)
+ {
+ const int r = pSrc[0], g = pSrc[1], b = pSrc[2];
+ pDst[0] = static_cast<uint8>((r * YR + g * YG + b * YB + 32768) >> 16);
+ pDst[1] = clamp(128 + ((r * CB_R + g * CB_G + b * CB_B + 32768) >> 16));
+ pDst[2] = clamp(128 + ((r * CR_R + g * CR_G + b * CR_B + 32768) >> 16));
+ }
+ }
+
+ static void RGBA_to_Y(uint8* pDst, const uint8* pSrc, int num_pixels)
+ {
+ for (; num_pixels; pDst++, pSrc += 4, num_pixels--)
+ pDst[0] = static_cast<uint8>((pSrc[0] * YR + pSrc[1] * YG + pSrc[2] * YB + 32768) >> 16);
+ }
+
+ static void Y_to_YCC(uint8* pDst, const uint8* pSrc, int num_pixels)
+ {
+ for (; num_pixels; pDst += 3, pSrc++, num_pixels--) { pDst[0] = pSrc[0]; pDst[1] = 128; pDst[2] = 128; }
+ }
+
+ // Forward DCT - DCT derived from jfdctint.
+ enum { CONST_BITS = 13, ROW_BITS = 2 };
+#define DCT_DESCALE(x, n) (((x) + (((int32)1) << ((n) - 1))) >> (n))
+#define DCT_MUL(var, c) (static_cast<int16>(var) * static_cast<int32>(c))
+#define DCT1D(s0, s1, s2, s3, s4, s5, s6, s7) \
+ int32 t0 = s0 + s7, t7 = s0 - s7, t1 = s1 + s6, t6 = s1 - s6, t2 = s2 + s5, t5 = s2 - s5, t3 = s3 + s4, t4 = s3 - s4; \
+ int32 t10 = t0 + t3, t13 = t0 - t3, t11 = t1 + t2, t12 = t1 - t2; \
+ int32 u1 = DCT_MUL(t12 + t13, 4433); \
+ s2 = u1 + DCT_MUL(t13, 6270); \
+ s6 = u1 + DCT_MUL(t12, -15137); \
+ u1 = t4 + t7; \
+ int32 u2 = t5 + t6, u3 = t4 + t6, u4 = t5 + t7; \
+ int32 z5 = DCT_MUL(u3 + u4, 9633); \
+ t4 = DCT_MUL(t4, 2446); t5 = DCT_MUL(t5, 16819); \
+ t6 = DCT_MUL(t6, 25172); t7 = DCT_MUL(t7, 12299); \
+ u1 = DCT_MUL(u1, -7373); u2 = DCT_MUL(u2, -20995); \
+ u3 = DCT_MUL(u3, -16069); u4 = DCT_MUL(u4, -3196); \
+ u3 += z5; u4 += z5; \
+ s0 = t10 + t11; s1 = t7 + u1 + u4; s3 = t6 + u2 + u3; s4 = t10 - t11; s5 = t5 + u2 + u4; s7 = t4 + u1 + u3;
+
+ static void DCT2D(int32* p)
+ {
+ int32 c, * q = p;
+ for (c = 7; c >= 0; c--, q += 8)
+ {
+ int32 s0 = q[0], s1 = q[1], s2 = q[2], s3 = q[3], s4 = q[4], s5 = q[5], s6 = q[6], s7 = q[7];
+ DCT1D(s0, s1, s2, s3, s4, s5, s6, s7);
+ q[0] = left_shifti(s0, ROW_BITS); q[1] = DCT_DESCALE(s1, CONST_BITS - ROW_BITS); q[2] = DCT_DESCALE(s2, CONST_BITS - ROW_BITS); q[3] = DCT_DESCALE(s3, CONST_BITS - ROW_BITS);
+ q[4] = left_shifti(s4, ROW_BITS); q[5] = DCT_DESCALE(s5, CONST_BITS - ROW_BITS); q[6] = DCT_DESCALE(s6, CONST_BITS - ROW_BITS); q[7] = DCT_DESCALE(s7, CONST_BITS - ROW_BITS);
+ }
+ for (q = p, c = 7; c >= 0; c--, q++)
+ {
+ int32 s0 = q[0 * 8], s1 = q[1 * 8], s2 = q[2 * 8], s3 = q[3 * 8], s4 = q[4 * 8], s5 = q[5 * 8], s6 = q[6 * 8], s7 = q[7 * 8];
+ DCT1D(s0, s1, s2, s3, s4, s5, s6, s7);
+ q[0 * 8] = DCT_DESCALE(s0, ROW_BITS + 3); q[1 * 8] = DCT_DESCALE(s1, CONST_BITS + ROW_BITS + 3); q[2 * 8] = DCT_DESCALE(s2, CONST_BITS + ROW_BITS + 3); q[3 * 8] = DCT_DESCALE(s3, CONST_BITS + ROW_BITS + 3);
+ q[4 * 8] = DCT_DESCALE(s4, ROW_BITS + 3); q[5 * 8] = DCT_DESCALE(s5, CONST_BITS + ROW_BITS + 3); q[6 * 8] = DCT_DESCALE(s6, CONST_BITS + ROW_BITS + 3); q[7 * 8] = DCT_DESCALE(s7, CONST_BITS + ROW_BITS + 3);
+ }
+ }
+
+ struct sym_freq { uint m_key, m_sym_index; };
+
+ // Radix sorts sym_freq[] array by 32-bit key m_key. Returns ptr to sorted values.
+ static inline sym_freq* radix_sort_syms(uint num_syms, sym_freq* pSyms0, sym_freq* pSyms1)
+ {
+ const uint cMaxPasses = 4;
+ uint32 hist[256 * cMaxPasses]; clear_obj(hist);
+ for (uint i = 0; i < num_syms; i++) { uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; hist[256 * 2 + ((freq >> 16) & 0xFF)]++; hist[256 * 3 + ((freq >> 24) & 0xFF)]++; }
+ sym_freq* pCur_syms = pSyms0, * pNew_syms = pSyms1;
+ uint total_passes = cMaxPasses; while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--;
+ for (uint pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8)
+ {
+ const uint32* pHist = &hist[pass << 8];
+ uint offsets[256], cur_ofs = 0;
+ for (uint i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; }
+ for (uint i = 0; i < num_syms; i++)
+ pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i];
+ sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t;
+ }
+ return pCur_syms;
+ }
+
+ // calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996.
+ static void calculate_minimum_redundancy(sym_freq* A, int n)
+ {
+ int root, leaf, next, avbl, used, dpth;
+ if (n == 0) return; else if (n == 1) { A[0].m_key = 1; return; }
+ A[0].m_key += A[1].m_key; root = 0; leaf = 2;
+ for (next = 1; next < n - 1; next++)
+ {
+ if (leaf >= n || A[root].m_key < A[leaf].m_key) { A[next].m_key = A[root].m_key; A[root++].m_key = next; }
+ else A[next].m_key = A[leaf++].m_key;
+ if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { A[next].m_key += A[root].m_key; A[root++].m_key = next; }
+ else A[next].m_key += A[leaf++].m_key;
+ }
+ A[n - 2].m_key = 0;
+ for (next = n - 3; next >= 0; next--) A[next].m_key = A[A[next].m_key].m_key + 1;
+ avbl = 1; used = dpth = 0; root = n - 2; next = n - 1;
+ while (avbl > 0)
+ {
+ while (root >= 0 && (int)A[root].m_key == dpth) { used++; root--; }
+ while (avbl > used) { A[next--].m_key = dpth; avbl--; }
+ avbl = 2 * used; dpth++; used = 0;
+ }
+ }
+
+ // Limits canonical Huffman code table's max code size to max_code_size.
+ static void huffman_enforce_max_code_size(int* pNum_codes, int code_list_len, int max_code_size)
+ {
+ if (code_list_len <= 1) return;
+
+ for (int i = max_code_size + 1; i <= MAX_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i];
+
+ uint32 total = 0;
+ for (int i = max_code_size; i > 0; i--)
+ total += (((uint32)pNum_codes[i]) << (max_code_size - i));
+
+ while (total != (1UL << max_code_size))
+ {
+ pNum_codes[max_code_size]--;
+ for (int i = max_code_size - 1; i > 0; i--)
+ {
+ if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; }
+ }
+ total--;
+ }
+ }
+
+ // Generates an optimized offman table.
+ void jpeg_encoder::optimize_huffman_table(int table_num, int table_len)
+ {
+ sym_freq syms0[MAX_HUFF_SYMBOLS], syms1[MAX_HUFF_SYMBOLS];
+ syms0[0].m_key = 1; syms0[0].m_sym_index = 0; // dummy symbol, assures that no valid code contains all 1's
+ int num_used_syms = 1;
+ const uint32* pSym_count = &m_huff_count[table_num][0];
+ for (int i = 0; i < table_len; i++)
+ if (pSym_count[i]) { syms0[num_used_syms].m_key = pSym_count[i]; syms0[num_used_syms++].m_sym_index = i + 1; }
+ sym_freq* pSyms = radix_sort_syms(num_used_syms, syms0, syms1);
+ calculate_minimum_redundancy(pSyms, num_used_syms);
+
+ // Count the # of symbols of each code size.
+ int num_codes[1 + MAX_HUFF_CODESIZE]; clear_obj(num_codes);
+ for (int i = 0; i < num_used_syms; i++)
+ num_codes[pSyms[i].m_key]++;
+
+ const uint JPGE_CODE_SIZE_LIMIT = 16; // the maximum possible size of a JPEG Huffman code (valid range is [9,16] - 9 vs. 8 because of the dummy symbol)
+ huffman_enforce_max_code_size(num_codes, num_used_syms, JPGE_CODE_SIZE_LIMIT);
+
+ // Compute m_huff_bits array, which contains the # of symbols per code size.
+ clear_obj(m_huff_bits[table_num]);
+ for (int i = 1; i <= (int)JPGE_CODE_SIZE_LIMIT; i++)
+ m_huff_bits[table_num][i] = static_cast<uint8>(num_codes[i]);
+
+ // Remove the dummy symbol added above, which must be in largest bucket.
+ for (int i = JPGE_CODE_SIZE_LIMIT; i >= 1; i--)
+ {
+ if (m_huff_bits[table_num][i]) { m_huff_bits[table_num][i]--; break; }
+ }
+
+ // Compute the m_huff_val array, which contains the symbol indices sorted by code size (smallest to largest).
+ for (int i = num_used_syms - 1; i >= 1; i--)
+ m_huff_val[table_num][num_used_syms - 1 - i] = static_cast<uint8>(pSyms[i].m_sym_index - 1);
+ }
+
+ // JPEG marker generation.
+ void jpeg_encoder::emit_byte(uint8 i)
+ {
+ m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_obj(i);
+ }
+
+ void jpeg_encoder::emit_word(uint i)
+ {
+ emit_byte(uint8(i >> 8)); emit_byte(uint8(i & 0xFF));
+ }
+
+ void jpeg_encoder::emit_marker(int marker)
+ {
+ emit_byte(uint8(0xFF)); emit_byte(uint8(marker));
+ }
+
+ // Emit JFIF marker
+ void jpeg_encoder::emit_jfif_app0()
+ {
+ emit_marker(M_APP0);
+ emit_word(2 + 4 + 1 + 2 + 1 + 2 + 2 + 1 + 1);
+ emit_byte(0x4A); emit_byte(0x46); emit_byte(0x49); emit_byte(0x46); /* Identifier: ASCII "JFIF" */
+ emit_byte(0);
+ emit_byte(1); /* Major version */
+ emit_byte(1); /* Minor version */
+ emit_byte(0); /* Density unit */
+ emit_word(1);
+ emit_word(1);
+ emit_byte(0); /* No thumbnail image */
+ emit_byte(0);
+ }
+
+ // Emit quantization tables
+ void jpeg_encoder::emit_dqt()
+ {
+ for (int i = 0; i < ((m_num_components == 3) ? 2 : 1); i++)
+ {
+ emit_marker(M_DQT);
+ emit_word(64 + 1 + 2);
+ emit_byte(static_cast<uint8>(i));
+ for (int j = 0; j < 64; j++)
+ emit_byte(static_cast<uint8>(m_quantization_tables[i][j]));
+ }
+ }
+
+ // Emit start of frame marker
+ void jpeg_encoder::emit_sof()
+ {
+ emit_marker(M_SOF0); /* baseline */
+ emit_word(3 * m_num_components + 2 + 5 + 1);
+ emit_byte(8); /* precision */
+ emit_word(m_image_y);
+ emit_word(m_image_x);
+ emit_byte(m_num_components);
+ for (int i = 0; i < m_num_components; i++)
+ {
+ emit_byte(static_cast<uint8>(i + 1)); /* component ID */
+ emit_byte((m_comp_h_samp[i] << 4) + m_comp_v_samp[i]); /* h and v sampling */
+ emit_byte(i > 0); /* quant. table num */
+ }
+ }
+
+ // Emit Huffman table.
+ void jpeg_encoder::emit_dht(uint8* bits, uint8* val, int index, bool ac_flag)
+ {
+ emit_marker(M_DHT);
+
+ int length = 0;
+ for (int i = 1; i <= 16; i++)
+ length += bits[i];
+
+ emit_word(length + 2 + 1 + 16);
+ emit_byte(static_cast<uint8>(index + (ac_flag << 4)));
+
+ for (int i = 1; i <= 16; i++)
+ emit_byte(bits[i]);
+
+ for (int i = 0; i < length; i++)
+ emit_byte(val[i]);
+ }
+
+ // Emit all Huffman tables.
+ void jpeg_encoder::emit_dhts()
+ {
+ emit_dht(m_huff_bits[0 + 0], m_huff_val[0 + 0], 0, false);
+ emit_dht(m_huff_bits[2 + 0], m_huff_val[2 + 0], 0, true);
+ if (m_num_components == 3)
+ {
+ emit_dht(m_huff_bits[0 + 1], m_huff_val[0 + 1], 1, false);
+ emit_dht(m_huff_bits[2 + 1], m_huff_val[2 + 1], 1, true);
+ }
+ }
+
+ // emit start of scan
+ void jpeg_encoder::emit_sos()
+ {
+ emit_marker(M_SOS);
+ emit_word(2 * m_num_components + 2 + 1 + 3);
+ emit_byte(m_num_components);
+ for (int i = 0; i < m_num_components; i++)
+ {
+ emit_byte(static_cast<uint8>(i + 1));
+ if (i == 0)
+ emit_byte((0 << 4) + 0);
+ else
+ emit_byte((1 << 4) + 1);
+ }
+ emit_byte(0); /* spectral selection */
+ emit_byte(63);
+ emit_byte(0);
+ }
+
+ // Emit all markers at beginning of image file.
+ void jpeg_encoder::emit_markers()
+ {
+ emit_marker(M_SOI);
+ emit_jfif_app0();
+ emit_dqt();
+ emit_sof();
+ emit_dhts();
+ emit_sos();
+ }
+
+ // Compute the actual canonical Huffman codes/code sizes given the JPEG huff bits and val arrays.
+ void jpeg_encoder::compute_huffman_table(uint* codes, uint8* code_sizes, uint8* bits, uint8* val)
+ {
+ int i, l, last_p, si;
+ uint8 huff_size[257];
+ uint huff_code[257];
+ uint code;
+
+ int p = 0;
+ for (l = 1; l <= 16; l++)
+ for (i = 1; i <= bits[l]; i++)
+ huff_size[p++] = (char)l;
+
+ huff_size[p] = 0; last_p = p; // write sentinel
+
+ code = 0; si = huff_size[0]; p = 0;
+
+ while (huff_size[p])
+ {
+ while (huff_size[p] == si)
+ huff_code[p++] = code++;
+ code <<= 1;
+ si++;
+ }
+
+ memset(codes, 0, sizeof(codes[0]) * 256);
+ memset(code_sizes, 0, sizeof(code_sizes[0]) * 256);
+ for (p = 0; p < last_p; p++)
+ {
+ codes[val[p]] = huff_code[p];
+ code_sizes[val[p]] = huff_size[p];
+ }
+ }
+
+ // Quantization table generation.
+ void jpeg_encoder::compute_quant_table(int32* pDst, int16* pSrc)
+ {
+ int32 q;
+ if (m_params.m_quality < 50)
+ q = 5000 / m_params.m_quality;
+ else
+ q = 200 - m_params.m_quality * 2;
+ for (int i = 0; i < 64; i++)
+ {
+ int32 j = *pSrc++; j = (j * q + 50L) / 100L;
+ *pDst++ = JPGE_MIN(JPGE_MAX(j, 1), 255);
+ }
+ }
+
+ // Higher-level methods.
+ void jpeg_encoder::first_pass_init()
+ {
+ m_bit_buffer = 0; m_bits_in = 0;
+ memset(m_last_dc_val, 0, 3 * sizeof(m_last_dc_val[0]));
+ m_mcu_y_ofs = 0;
+ m_pass_num = 1;
+ }
+
+ bool jpeg_encoder::second_pass_init()
+ {
+ compute_huffman_table(&m_huff_codes[0 + 0][0], &m_huff_code_sizes[0 + 0][0], m_huff_bits[0 + 0], m_huff_val[0 + 0]);
+ compute_huffman_table(&m_huff_codes[2 + 0][0], &m_huff_code_sizes[2 + 0][0], m_huff_bits[2 + 0], m_huff_val[2 + 0]);
+ if (m_num_components > 1)
+ {
+ compute_huffman_table(&m_huff_codes[0 + 1][0], &m_huff_code_sizes[0 + 1][0], m_huff_bits[0 + 1], m_huff_val[0 + 1]);
+ compute_huffman_table(&m_huff_codes[2 + 1][0], &m_huff_code_sizes[2 + 1][0], m_huff_bits[2 + 1], m_huff_val[2 + 1]);
+ }
+ first_pass_init();
+ emit_markers();
+ m_pass_num = 2;
+ return true;
+ }
+
+ bool jpeg_encoder::jpg_open(int p_x_res, int p_y_res, int src_channels)
+ {
+ m_num_components = 3;
+ switch (m_params.m_subsampling)
+ {
+ case Y_ONLY:
+ {
+ m_num_components = 1;
+ m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1;
+ m_mcu_x = 8; m_mcu_y = 8;
+ break;
+ }
+ case H1V1:
+ {
+ m_comp_h_samp[0] = 1; m_comp_v_samp[0] = 1;
+ m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
+ m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
+ m_mcu_x = 8; m_mcu_y = 8;
+ break;
+ }
+ case H2V1:
+ {
+ m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 1;
+ m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
+ m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
+ m_mcu_x = 16; m_mcu_y = 8;
+ break;
+ }
+ case H2V2:
+ {
+ m_comp_h_samp[0] = 2; m_comp_v_samp[0] = 2;
+ m_comp_h_samp[1] = 1; m_comp_v_samp[1] = 1;
+ m_comp_h_samp[2] = 1; m_comp_v_samp[2] = 1;
+ m_mcu_x = 16; m_mcu_y = 16;
+ }
+ }
+
+ m_image_x = p_x_res; m_image_y = p_y_res;
+ m_image_bpp = src_channels;
+ m_image_bpl = m_image_x * src_channels;
+ m_image_x_mcu = (m_image_x + m_mcu_x - 1) & (~(m_mcu_x - 1));
+ m_image_y_mcu = (m_image_y + m_mcu_y - 1) & (~(m_mcu_y - 1));
+ m_image_bpl_xlt = m_image_x * m_num_components;
+ m_image_bpl_mcu = m_image_x_mcu * m_num_components;
+ m_mcus_per_row = m_image_x_mcu / m_mcu_x;
+
+ if ((m_mcu_lines[0] = static_cast<uint8*>(jpge_malloc(m_image_bpl_mcu * m_mcu_y))) == NULL) return false;
+ for (int i = 1; i < m_mcu_y; i++)
+ m_mcu_lines[i] = m_mcu_lines[i - 1] + m_image_bpl_mcu;
+
+ if (m_params.m_use_std_tables)
+ {
+ compute_quant_table(m_quantization_tables[0], s_std_lum_quant);
+ compute_quant_table(m_quantization_tables[1], m_params.m_no_chroma_discrim_flag ? s_std_lum_quant : s_std_croma_quant);
+ }
+ else
+ {
+ compute_quant_table(m_quantization_tables[0], s_alt_quant);
+ memcpy(m_quantization_tables[1], m_quantization_tables[0], sizeof(m_quantization_tables[1]));
+ }
+
+ m_out_buf_left = JPGE_OUT_BUF_SIZE;
+ m_pOut_buf = m_out_buf;
+
+ if (m_params.m_two_pass_flag)
+ {
+ clear_obj(m_huff_count);
+ first_pass_init();
+ }
+ else
+ {
+ memcpy(m_huff_bits[0 + 0], s_dc_lum_bits, 17); memcpy(m_huff_val[0 + 0], s_dc_lum_val, DC_LUM_CODES);
+ memcpy(m_huff_bits[2 + 0], s_ac_lum_bits, 17); memcpy(m_huff_val[2 + 0], s_ac_lum_val, AC_LUM_CODES);
+ memcpy(m_huff_bits[0 + 1], s_dc_chroma_bits, 17); memcpy(m_huff_val[0 + 1], s_dc_chroma_val, DC_CHROMA_CODES);
+ memcpy(m_huff_bits[2 + 1], s_ac_chroma_bits, 17); memcpy(m_huff_val[2 + 1], s_ac_chroma_val, AC_CHROMA_CODES);
+ if (!second_pass_init()) return false; // in effect, skip over the first pass
+ }
+ return m_all_stream_writes_succeeded;
+ }
+
+ void jpeg_encoder::load_block_8_8_grey(int x)
+ {
+ uint8* pSrc;
+ sample_array_t* pDst = m_sample_array;
+ x <<= 3;
+ for (int i = 0; i < 8; i++, pDst += 8)
+ {
+ pSrc = m_mcu_lines[i] + x;
+ pDst[0] = pSrc[0] - 128; pDst[1] = pSrc[1] - 128; pDst[2] = pSrc[2] - 128; pDst[3] = pSrc[3] - 128;
+ pDst[4] = pSrc[4] - 128; pDst[5] = pSrc[5] - 128; pDst[6] = pSrc[6] - 128; pDst[7] = pSrc[7] - 128;
+ }
+ }
+
+ void jpeg_encoder::load_block_8_8(int x, int y, int c)
+ {
+ uint8* pSrc;
+ sample_array_t* pDst = m_sample_array;
+ x = (x * (8 * 3)) + c;
+ y <<= 3;
+ for (int i = 0; i < 8; i++, pDst += 8)
+ {
+ pSrc = m_mcu_lines[y + i] + x;
+ pDst[0] = pSrc[0 * 3] - 128; pDst[1] = pSrc[1 * 3] - 128; pDst[2] = pSrc[2 * 3] - 128; pDst[3] = pSrc[3 * 3] - 128;
+ pDst[4] = pSrc[4 * 3] - 128; pDst[5] = pSrc[5 * 3] - 128; pDst[6] = pSrc[6 * 3] - 128; pDst[7] = pSrc[7 * 3] - 128;
+ }
+ }
+
+ void jpeg_encoder::load_block_16_8(int x, int c)
+ {
+ uint8* pSrc1, * pSrc2;
+ sample_array_t* pDst = m_sample_array;
+ x = (x * (16 * 3)) + c;
+ for (int i = 0; i < 16; i += 2, pDst += 8)
+ {
+ pSrc1 = m_mcu_lines[i + 0] + x;
+ pSrc2 = m_mcu_lines[i + 1] + x;
+ pDst[0] = ((pSrc1[0 * 3] + pSrc1[1 * 3] + pSrc2[0 * 3] + pSrc2[1 * 3] + 2) >> 2) - 128; pDst[1] = ((pSrc1[2 * 3] + pSrc1[3 * 3] + pSrc2[2 * 3] + pSrc2[3 * 3] + 2) >> 2) - 128;
+ pDst[2] = ((pSrc1[4 * 3] + pSrc1[5 * 3] + pSrc2[4 * 3] + pSrc2[5 * 3] + 2) >> 2) - 128; pDst[3] = ((pSrc1[6 * 3] + pSrc1[7 * 3] + pSrc2[6 * 3] + pSrc2[7 * 3] + 2) >> 2) - 128;
+ pDst[4] = ((pSrc1[8 * 3] + pSrc1[9 * 3] + pSrc2[8 * 3] + pSrc2[9 * 3] + 2) >> 2) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3] + pSrc2[10 * 3] + pSrc2[11 * 3] + 2) >> 2) - 128;
+ pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3] + pSrc2[12 * 3] + pSrc2[13 * 3] + 2) >> 2) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3] + pSrc2[14 * 3] + pSrc2[15 * 3] + 2) >> 2) - 128;
+ }
+ }
+
+ void jpeg_encoder::load_block_16_8_8(int x, int c)
+ {
+ uint8* pSrc1;
+ sample_array_t* pDst = m_sample_array;
+ x = (x * (16 * 3)) + c;
+ for (int i = 0; i < 8; i++, pDst += 8)
+ {
+ pSrc1 = m_mcu_lines[i + 0] + x;
+ pDst[0] = ((pSrc1[0 * 3] + pSrc1[1 * 3] + 1) >> 1) - 128; pDst[1] = ((pSrc1[2 * 3] + pSrc1[3 * 3] + 1) >> 1) - 128;
+ pDst[2] = ((pSrc1[4 * 3] + pSrc1[5 * 3] + 1) >> 1) - 128; pDst[3] = ((pSrc1[6 * 3] + pSrc1[7 * 3] + 1) >> 1) - 128;
+ pDst[4] = ((pSrc1[8 * 3] + pSrc1[9 * 3] + 1) >> 1) - 128; pDst[5] = ((pSrc1[10 * 3] + pSrc1[11 * 3] + 1) >> 1) - 128;
+ pDst[6] = ((pSrc1[12 * 3] + pSrc1[13 * 3] + 1) >> 1) - 128; pDst[7] = ((pSrc1[14 * 3] + pSrc1[15 * 3] + 1) >> 1) - 128;
+ }
+ }
+
+ void jpeg_encoder::load_quantized_coefficients(int component_num)
+ {
+ int32* q = m_quantization_tables[component_num > 0];
+ int16* pDst = m_coefficient_array;
+ for (int i = 0; i < 64; i++)
+ {
+ sample_array_t j = m_sample_array[s_zag[i]];
+ if (j < 0)
+ {
+ if ((j = -j + (*q >> 1)) < *q)
+ *pDst++ = 0;
+ else
+ *pDst++ = static_cast<int16>(-(j / *q));
+ }
+ else
+ {
+ if ((j = j + (*q >> 1)) < *q)
+ *pDst++ = 0;
+ else
+ *pDst++ = static_cast<int16>((j / *q));
+ }
+ q++;
+ }
+ }
+
+ void jpeg_encoder::flush_output_buffer()
+ {
+ if (m_out_buf_left != JPGE_OUT_BUF_SIZE)
+ m_all_stream_writes_succeeded = m_all_stream_writes_succeeded && m_pStream->put_buf(m_out_buf, JPGE_OUT_BUF_SIZE - m_out_buf_left);
+ m_pOut_buf = m_out_buf;
+ m_out_buf_left = JPGE_OUT_BUF_SIZE;
+ }
+
+ void jpeg_encoder::put_bits(uint bits, uint len)
+ {
+ m_bit_buffer |= ((uint32)bits << (24 - (m_bits_in += len)));
+ while (m_bits_in >= 8)
+ {
+ uint8 c;
+#define JPGE_PUT_BYTE(c) { *m_pOut_buf++ = (c); if (--m_out_buf_left == 0) flush_output_buffer(); }
+ JPGE_PUT_BYTE(c = (uint8)((m_bit_buffer >> 16) & 0xFF));
+ if (c == 0xFF) JPGE_PUT_BYTE(0);
+ m_bit_buffer <<= 8;
+ m_bits_in -= 8;
+ }
+ }
+
+ void jpeg_encoder::code_coefficients_pass_one(int component_num)
+ {
+ if (component_num >= 3) return; // just to shut up static analysis
+ int i, run_len, nbits, temp1;
+ int16* src = m_coefficient_array;
+ uint32* dc_count = component_num ? m_huff_count[0 + 1] : m_huff_count[0 + 0], * ac_count = component_num ? m_huff_count[2 + 1] : m_huff_count[2 + 0];
+
+ temp1 = src[0] - m_last_dc_val[component_num];
+ m_last_dc_val[component_num] = src[0];
+ if (temp1 < 0) temp1 = -temp1;
+
+ nbits = 0;
+ while (temp1)
+ {
+ nbits++; temp1 >>= 1;
+ }
+
+ dc_count[nbits]++;
+ for (run_len = 0, i = 1; i < 64; i++)
+ {
+ if ((temp1 = m_coefficient_array[i]) == 0)
+ run_len++;
+ else
+ {
+ while (run_len >= 16)
+ {
+ ac_count[0xF0]++;
+ run_len -= 16;
+ }
+ if (temp1 < 0) temp1 = -temp1;
+ nbits = 1;
+ while (temp1 >>= 1) nbits++;
+ ac_count[(run_len << 4) + nbits]++;
+ run_len = 0;
+ }
+ }
+ if (run_len) ac_count[0]++;
+ }
+
+ void jpeg_encoder::code_coefficients_pass_two(int component_num)
+ {
+ int i, j, run_len, nbits, temp1, temp2;
+ int16* pSrc = m_coefficient_array;
+ uint* codes[2];
+ uint8* code_sizes[2];
+
+ if (component_num == 0)
+ {
+ codes[0] = m_huff_codes[0 + 0]; codes[1] = m_huff_codes[2 + 0];
+ code_sizes[0] = m_huff_code_sizes[0 + 0]; code_sizes[1] = m_huff_code_sizes[2 + 0];
+ }
+ else
+ {
+ codes[0] = m_huff_codes[0 + 1]; codes[1] = m_huff_codes[2 + 1];
+ code_sizes[0] = m_huff_code_sizes[0 + 1]; code_sizes[1] = m_huff_code_sizes[2 + 1];
+ }
+
+ temp1 = temp2 = pSrc[0] - m_last_dc_val[component_num];
+ m_last_dc_val[component_num] = pSrc[0];
+
+ if (temp1 < 0)
+ {
+ temp1 = -temp1; temp2--;
+ }
+
+ nbits = 0;
+ while (temp1)
+ {
+ nbits++; temp1 >>= 1;
+ }
+
+ put_bits(codes[0][nbits], code_sizes[0][nbits]);
+ if (nbits) put_bits(temp2 & ((1 << nbits) - 1), nbits);
+
+ for (run_len = 0, i = 1; i < 64; i++)
+ {
+ if ((temp1 = m_coefficient_array[i]) == 0)
+ run_len++;
+ else
+ {
+ while (run_len >= 16)
+ {
+ put_bits(codes[1][0xF0], code_sizes[1][0xF0]);
+ run_len -= 16;
+ }
+ if ((temp2 = temp1) < 0)
+ {
+ temp1 = -temp1;
+ temp2--;
+ }
+ nbits = 1;
+ while (temp1 >>= 1)
+ nbits++;
+ j = (run_len << 4) + nbits;
+ put_bits(codes[1][j], code_sizes[1][j]);
+ put_bits(temp2 & ((1 << nbits) - 1), nbits);
+ run_len = 0;
+ }
+ }
+ if (run_len)
+ put_bits(codes[1][0], code_sizes[1][0]);
+ }
+
+ void jpeg_encoder::code_block(int component_num)
+ {
+ DCT2D(m_sample_array);
+ load_quantized_coefficients(component_num);
+ if (m_pass_num == 1)
+ code_coefficients_pass_one(component_num);
+ else
+ code_coefficients_pass_two(component_num);
+ }
+
+ void jpeg_encoder::process_mcu_row()
+ {
+ if (m_num_components == 1)
+ {
+ for (int i = 0; i < m_mcus_per_row; i++)
+ {
+ load_block_8_8_grey(i); code_block(0);
+ }
+ }
+ else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1))
+ {
+ for (int i = 0; i < m_mcus_per_row; i++)
+ {
+ load_block_8_8(i, 0, 0); code_block(0); load_block_8_8(i, 0, 1); code_block(1); load_block_8_8(i, 0, 2); code_block(2);
+ }
+ }
+ else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1))
+ {
+ for (int i = 0; i < m_mcus_per_row; i++)
+ {
+ load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0);
+ load_block_16_8_8(i, 1); code_block(1); load_block_16_8_8(i, 2); code_block(2);
+ }
+ }
+ else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2))
+ {
+ for (int i = 0; i < m_mcus_per_row; i++)
+ {
+ load_block_8_8(i * 2 + 0, 0, 0); code_block(0); load_block_8_8(i * 2 + 1, 0, 0); code_block(0);
+ load_block_8_8(i * 2 + 0, 1, 0); code_block(0); load_block_8_8(i * 2 + 1, 1, 0); code_block(0);
+ load_block_16_8(i, 1); code_block(1); load_block_16_8(i, 2); code_block(2);
+ }
+ }
+ }
+
+ bool jpeg_encoder::terminate_pass_one()
+ {
+ optimize_huffman_table(0 + 0, DC_LUM_CODES); optimize_huffman_table(2 + 0, AC_LUM_CODES);
+ if (m_num_components > 1)
+ {
+ optimize_huffman_table(0 + 1, DC_CHROMA_CODES); optimize_huffman_table(2 + 1, AC_CHROMA_CODES);
+ }
+ return second_pass_init();
+ }
+
+ bool jpeg_encoder::terminate_pass_two()
+ {
+ put_bits(0x7F, 7);
+ flush_output_buffer();
+ emit_marker(M_EOI);
+ m_pass_num++; // purposely bump up m_pass_num, for debugging
+ return true;
+ }
+
+ bool jpeg_encoder::process_end_of_image()
+ {
+ if (m_mcu_y_ofs)
+ {
+ if (m_mcu_y_ofs < 16) // check here just to shut up static analysis
+ {
+ for (int i = m_mcu_y_ofs; i < m_mcu_y; i++)
+ memcpy(m_mcu_lines[i], m_mcu_lines[m_mcu_y_ofs - 1], m_image_bpl_mcu);
+ }
+
+ process_mcu_row();
+ }
+
+ if (m_pass_num == 1)
+ return terminate_pass_one();
+ else
+ return terminate_pass_two();
+ }
+
+ void jpeg_encoder::load_mcu(const void* pSrc)
+ {
+ const uint8* Psrc = reinterpret_cast<const uint8*>(pSrc);
+
+ uint8* pDst = m_mcu_lines[m_mcu_y_ofs]; // OK to write up to m_image_bpl_xlt bytes to pDst
+
+ if (m_num_components == 1)
+ {
+ if (m_image_bpp == 4)
+ RGBA_to_Y(pDst, Psrc, m_image_x);
+ else if (m_image_bpp == 3)
+ RGB_to_Y(pDst, Psrc, m_image_x);
+ else
+ memcpy(pDst, Psrc, m_image_x);
+ }
+ else
+ {
+ if (m_image_bpp == 4)
+ RGBA_to_YCC(pDst, Psrc, m_image_x);
+ else if (m_image_bpp == 3)
+ RGB_to_YCC(pDst, Psrc, m_image_x);
+ else
+ Y_to_YCC(pDst, Psrc, m_image_x);
+ }
+
+ // Possibly duplicate pixels at end of scanline if not a multiple of 8 or 16
+ if (m_num_components == 1)
+ memset(m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt, pDst[m_image_bpl_xlt - 1], m_image_x_mcu - m_image_x);
+ else
+ {
+ const uint8 y = pDst[m_image_bpl_xlt - 3 + 0], cb = pDst[m_image_bpl_xlt - 3 + 1], cr = pDst[m_image_bpl_xlt - 3 + 2];
+ uint8* q = m_mcu_lines[m_mcu_y_ofs] + m_image_bpl_xlt;
+ for (int i = m_image_x; i < m_image_x_mcu; i++)
+ {
+ *q++ = y; *q++ = cb; *q++ = cr;
+ }
+ }
+
+ if (++m_mcu_y_ofs == m_mcu_y)
+ {
+ process_mcu_row();
+ m_mcu_y_ofs = 0;
+ }
+ }
+
+ void jpeg_encoder::clear()
+ {
+ m_mcu_lines[0] = NULL;
+ m_pass_num = 0;
+ m_all_stream_writes_succeeded = true;
+ }
+
+ jpeg_encoder::jpeg_encoder()
+ {
+ clear();
+ }
+
+ jpeg_encoder::~jpeg_encoder()
+ {
+ deinit();
+ }
+
+ bool jpeg_encoder::init(output_stream* pStream, int width, int height, int src_channels, const params& comp_params)
+ {
+ deinit();
+ if (((!pStream) || (width < 1) || (height < 1)) || ((src_channels != 1) && (src_channels != 3) && (src_channels != 4)) || (!comp_params.check())) return false;
+ m_pStream = pStream;
+ m_params = comp_params;
+ return jpg_open(width, height, src_channels);
+ }
+
+ void jpeg_encoder::deinit()
+ {
+ jpge_free(m_mcu_lines[0]);
+ clear();
+ }
+
+ bool jpeg_encoder::process_scanline(const void* pScanline)
+ {
+ if ((m_pass_num < 1) || (m_pass_num > 2)) return false;
+ if (m_all_stream_writes_succeeded)
+ {
+ if (!pScanline)
+ {
+ if (!process_end_of_image()) return false;
+ }
+ else
+ {
+ load_mcu(pScanline);
+ }
+ }
+ return m_all_stream_writes_succeeded;
+ }
+
+ // Higher level wrappers/examples (optional).
+#include <stdio.h>
+
+ class cfile_stream : public output_stream
+ {
+ cfile_stream(const cfile_stream&);
+ cfile_stream& operator= (const cfile_stream&);
+
+ FILE* m_pFile;
+ bool m_bStatus;
+
+ public:
+ cfile_stream() : m_pFile(NULL), m_bStatus(false) { }
+
+ virtual ~cfile_stream()
+ {
+ close();
+ }
+
+ bool open(const char* pFilename)
+ {
+ close();
+ m_pFile = fopen(pFilename, "wb");
+ m_bStatus = (m_pFile != NULL);
+ return m_bStatus;
+ }
+
+ bool close()
+ {
+ if (m_pFile)
+ {
+ if (fclose(m_pFile) == EOF)
+ {
+ m_bStatus = false;
+ }
+ m_pFile = NULL;
+ }
+ return m_bStatus;
+ }
+
+ virtual bool put_buf(const void* pBuf, int len)
+ {
+ m_bStatus = m_bStatus && (fwrite(pBuf, len, 1, m_pFile) == 1);
+ return m_bStatus;
+ }
+
+ uint get_size() const
+ {
+ return m_pFile ? ftell(m_pFile) : 0;
+ }
+ };
+
+ // Writes JPEG image to file.
+ bool compress_image_to_jpeg_file(const char* pFilename, int width, int height, int num_channels, const uint8* pImage_data, const params& comp_params)
+ {
+ cfile_stream dst_stream;
+ if (!dst_stream.open(pFilename))
+ return false;
+
+ jpge::jpeg_encoder dst_image;
+ if (!dst_image.init(&dst_stream, width, height, num_channels, comp_params))
+ return false;
+
+ for (uint pass_index = 0; pass_index < dst_image.get_total_passes(); pass_index++)
+ {
+ for (int i = 0; i < height; i++)
+ {
+ const uint8* pBuf = pImage_data + i * width * num_channels;
+ if (!dst_image.process_scanline(pBuf))
+ return false;
+ }
+ if (!dst_image.process_scanline(NULL))
+ return false;
+ }
+
+ dst_image.deinit();
+
+ return dst_stream.close();
+ }
+
+ class memory_stream : public output_stream
+ {
+ memory_stream(const memory_stream&);
+ memory_stream& operator= (const memory_stream&);
+
+ uint8* m_pBuf;
+ uint m_buf_size, m_buf_ofs;
+
+ public:
+ memory_stream(void* pBuf, uint buf_size) : m_pBuf(static_cast<uint8*>(pBuf)), m_buf_size(buf_size), m_buf_ofs(0) { }
+
+ virtual ~memory_stream() { }
+
+ virtual bool put_buf(const void* pBuf, int len)
+ {
+ uint buf_remaining = m_buf_size - m_buf_ofs;
+ if ((uint)len > buf_remaining)
+ return false;
+ memcpy(m_pBuf + m_buf_ofs, pBuf, len);
+ m_buf_ofs += len;
+ return true;
+ }
+
+ uint get_size() const
+ {
+ return m_buf_ofs;
+ }
+ };
+
+ bool compress_image_to_jpeg_file_in_memory(void* pDstBuf, int& buf_size, int width, int height, int num_channels, const uint8* pImage_data, const params& comp_params)
+ {
+ if ((!pDstBuf) || (!buf_size))
+ return false;
+
+ memory_stream dst_stream(pDstBuf, buf_size);
+
+ buf_size = 0;
+
+ jpge::jpeg_encoder dst_image;
+ if (!dst_image.init(&dst_stream, width, height, num_channels, comp_params))
+ return false;
+
+ for (uint pass_index = 0; pass_index < dst_image.get_total_passes(); pass_index++)
+ {
+ for (int i = 0; i < height; i++)
+ {
+ const uint8* pScanline = pImage_data + i * width * num_channels;
+ if (!dst_image.process_scanline(pScanline))
+ return false;
+ }
+ if (!dst_image.process_scanline(NULL))
+ return false;
+ }
+
+ dst_image.deinit();
+
+ buf_size = dst_stream.get_size();
+ return true;
+ }
+
+} // namespace jpge
+
diff --git a/thirdparty/jpeg-compressor/jpge.h b/thirdparty/jpeg-compressor/jpge.h
new file mode 100644
index 0000000000..d10510e553
--- /dev/null
+++ b/thirdparty/jpeg-compressor/jpge.h
@@ -0,0 +1,174 @@
+// jpge.h - C++ class for JPEG compression.
+// Public Domain or Apache 2.0, Richard Geldreich <richgel99@gmail.com>
+// Alex Evans: Added RGBA support, linear memory allocator.
+#ifndef JPEG_ENCODER_H
+#define JPEG_ENCODER_H
+
+namespace jpge
+{
+ typedef unsigned char uint8;
+ typedef signed short int16;
+ typedef signed int int32;
+ typedef unsigned short uint16;
+ typedef unsigned int uint32;
+ typedef unsigned int uint;
+
+ // JPEG chroma subsampling factors. Y_ONLY (grayscale images) and H2V2 (color images) are the most common.
+ enum subsampling_t { Y_ONLY = 0, H1V1 = 1, H2V1 = 2, H2V2 = 3 };
+
+ // JPEG compression parameters structure.
+ struct params
+ {
+ inline params() : m_quality(85), m_subsampling(H2V2), m_no_chroma_discrim_flag(false), m_two_pass_flag(false), m_use_std_tables(false) { }
+
+ inline bool check() const
+ {
+ if ((m_quality < 1) || (m_quality > 100)) return false;
+ if ((uint)m_subsampling > (uint)H2V2) return false;
+ return true;
+ }
+
+ // Quality: 1-100, higher is better. Typical values are around 50-95.
+ int m_quality;
+
+ // m_subsampling:
+ // 0 = Y (grayscale) only
+ // 1 = YCbCr, no subsampling (H1V1, YCbCr 1x1x1, 3 blocks per MCU)
+ // 2 = YCbCr, H2V1 subsampling (YCbCr 2x1x1, 4 blocks per MCU)
+ // 3 = YCbCr, H2V2 subsampling (YCbCr 4x1x1, 6 blocks per MCU-- very common)
+ subsampling_t m_subsampling;
+
+ // Disables CbCr discrimination - only intended for testing.
+ // If true, the Y quantization table is also used for the CbCr channels.
+ bool m_no_chroma_discrim_flag;
+
+ bool m_two_pass_flag;
+
+ // By default we use the same quantization tables as mozjpeg's default.
+ // Set to true to use the traditional tables from JPEG Annex K.
+ bool m_use_std_tables;
+ };
+
+ // Writes JPEG image to a file.
+ // num_channels must be 1 (Y) or 3 (RGB), image pitch must be width*num_channels.
+ bool compress_image_to_jpeg_file(const char* pFilename, int width, int height, int num_channels, const uint8* pImage_data, const params& comp_params = params());
+
+ // Writes JPEG image to memory buffer.
+ // On entry, buf_size is the size of the output buffer pointed at by pBuf, which should be at least ~1024 bytes.
+ // If return value is true, buf_size will be set to the size of the compressed data.
+ bool compress_image_to_jpeg_file_in_memory(void* pBuf, int& buf_size, int width, int height, int num_channels, const uint8* pImage_data, const params& comp_params = params());
+
+ // Output stream abstract class - used by the jpeg_encoder class to write to the output stream.
+ // put_buf() is generally called with len==JPGE_OUT_BUF_SIZE bytes, but for headers it'll be called with smaller amounts.
+ class output_stream
+ {
+ public:
+ virtual ~output_stream() { };
+ virtual bool put_buf(const void* Pbuf, int len) = 0;
+ template<class T> inline bool put_obj(const T& obj) { return put_buf(&obj, sizeof(T)); }
+ };
+
+ // Lower level jpeg_encoder class - useful if more control is needed than the above helper functions.
+ class jpeg_encoder
+ {
+ public:
+ jpeg_encoder();
+ ~jpeg_encoder();
+
+ // Initializes the compressor.
+ // pStream: The stream object to use for writing compressed data.
+ // params - Compression parameters structure, defined above.
+ // width, height - Image dimensions.
+ // channels - May be 1, or 3. 1 indicates grayscale, 3 indicates RGB source data.
+ // Returns false on out of memory or if a stream write fails.
+ bool init(output_stream* pStream, int width, int height, int src_channels, const params& comp_params = params());
+
+ const params& get_params() const { return m_params; }
+
+ // Deinitializes the compressor, freeing any allocated memory. May be called at any time.
+ void deinit();
+
+ uint get_total_passes() const { return m_params.m_two_pass_flag ? 2 : 1; }
+ inline uint get_cur_pass() { return m_pass_num; }
+
+ // Call this method with each source scanline.
+ // width * src_channels bytes per scanline is expected (RGB or Y format).
+ // You must call with NULL after all scanlines are processed to finish compression.
+ // Returns false on out of memory or if a stream write fails.
+ bool process_scanline(const void* pScanline);
+
+ private:
+ jpeg_encoder(const jpeg_encoder&);
+ jpeg_encoder& operator =(const jpeg_encoder&);
+
+ typedef int32 sample_array_t;
+
+ output_stream* m_pStream;
+ params m_params;
+ uint8 m_num_components;
+ uint8 m_comp_h_samp[3], m_comp_v_samp[3];
+ int m_image_x, m_image_y, m_image_bpp, m_image_bpl;
+ int m_image_x_mcu, m_image_y_mcu;
+ int m_image_bpl_xlt, m_image_bpl_mcu;
+ int m_mcus_per_row;
+ int m_mcu_x, m_mcu_y;
+ uint8* m_mcu_lines[16];
+ uint8 m_mcu_y_ofs;
+ sample_array_t m_sample_array[64];
+ int16 m_coefficient_array[64];
+ int32 m_quantization_tables[2][64];
+ uint m_huff_codes[4][256];
+ uint8 m_huff_code_sizes[4][256];
+ uint8 m_huff_bits[4][17];
+ uint8 m_huff_val[4][256];
+ uint32 m_huff_count[4][256];
+ int m_last_dc_val[3];
+ enum { JPGE_OUT_BUF_SIZE = 2048 };
+ uint8 m_out_buf[JPGE_OUT_BUF_SIZE];
+ uint8* m_pOut_buf;
+ uint m_out_buf_left;
+ uint32 m_bit_buffer;
+ uint m_bits_in;
+ uint8 m_pass_num;
+ bool m_all_stream_writes_succeeded;
+
+ void optimize_huffman_table(int table_num, int table_len);
+ void emit_byte(uint8 i);
+ void emit_word(uint i);
+ void emit_marker(int marker);
+ void emit_jfif_app0();
+ void emit_dqt();
+ void emit_sof();
+ void emit_dht(uint8* bits, uint8* val, int index, bool ac_flag);
+ void emit_dhts();
+ void emit_sos();
+ void emit_markers();
+ void compute_huffman_table(uint* codes, uint8* code_sizes, uint8* bits, uint8* val);
+ void compute_quant_table(int32* dst, int16* src);
+ void adjust_quant_table(int32* dst, int32* src);
+ void first_pass_init();
+ bool second_pass_init();
+ bool jpg_open(int p_x_res, int p_y_res, int src_channels);
+ void load_block_8_8_grey(int x);
+ void load_block_8_8(int x, int y, int c);
+ void load_block_16_8(int x, int c);
+ void load_block_16_8_8(int x, int c);
+ void load_quantized_coefficients(int component_num);
+ void flush_output_buffer();
+ void put_bits(uint bits, uint len);
+ void code_coefficients_pass_one(int component_num);
+ void code_coefficients_pass_two(int component_num);
+ void code_block(int component_num);
+ void process_mcu_row();
+ bool terminate_pass_one();
+ bool terminate_pass_two();
+ bool process_end_of_image();
+ void load_mcu(const void* src);
+ void clear();
+ void init();
+ };
+
+} // namespace jpge
+
+#endif // JPEG_ENCODER
+
diff --git a/thirdparty/openxr/include/openxr/openxr.h b/thirdparty/openxr/include/openxr/openxr.h
index 8798e5a6e0..6c6a52d27e 100644
--- a/thirdparty/openxr/include/openxr/openxr.h
+++ b/thirdparty/openxr/include/openxr/openxr.h
@@ -25,12 +25,15 @@ extern "C" {
((((major) & 0xffffULL) << 48) | (((minor) & 0xffffULL) << 32) | ((patch) & 0xffffffffULL))
// OpenXR current version number.
-#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 22)
+#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 23)
#define XR_VERSION_MAJOR(version) (uint16_t)(((uint64_t)(version) >> 48)& 0xffffULL)
#define XR_VERSION_MINOR(version) (uint16_t)(((uint64_t)(version) >> 32) & 0xffffULL)
#define XR_VERSION_PATCH(version) (uint32_t)((uint64_t)(version) & 0xffffffffULL)
+#define XR_MIN_COMPOSITION_LAYERS_SUPPORTED 16
+
+
#if !defined(XR_NULL_HANDLE)
#if (XR_PTR_SIZE == 8) && XR_CPP_NULLPTR_SUPPORTED
#define XR_NULL_HANDLE nullptr
@@ -120,7 +123,6 @@ XR_DEFINE_HANDLE(XrActionSet)
#define XR_MAX_PATH_LENGTH 256
#define XR_MAX_STRUCTURE_NAME_SIZE 64
#define XR_MAX_RESULT_STRING_SIZE 64
-#define XR_MIN_COMPOSITION_LAYERS_SUPPORTED 16
#define XR_MAX_ACTION_SET_NAME_SIZE 64
#define XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE 128
#define XR_MAX_ACTION_NAME_SIZE 64
@@ -196,6 +198,10 @@ typedef enum XrResult {
XR_ERROR_SCENE_COMPUTE_CONSISTENCY_MISMATCH_MSFT = -1000097005,
XR_ERROR_DISPLAY_REFRESH_RATE_UNSUPPORTED_FB = -1000101000,
XR_ERROR_COLOR_SPACE_UNSUPPORTED_FB = -1000108000,
+ XR_ERROR_SPACE_COMPONENT_NOT_SUPPORTED_FB = -1000113000,
+ XR_ERROR_SPACE_COMPONENT_NOT_ENABLED_FB = -1000113001,
+ XR_ERROR_SPACE_COMPONENT_STATUS_PENDING_FB = -1000113002,
+ XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB = -1000113003,
XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB = -1000118000,
XR_ERROR_FEATURE_ALREADY_CREATED_PASSTHROUGH_FB = -1000118001,
XR_ERROR_FEATURE_REQUIRED_PASSTHROUGH_FB = -1000118002,
@@ -305,6 +311,9 @@ typedef enum XrStructureType {
XR_TYPE_VIEW_CONFIGURATION_DEPTH_RANGE_EXT = 1000046000,
XR_TYPE_GRAPHICS_BINDING_EGL_MNDX = 1000048004,
XR_TYPE_SPATIAL_GRAPH_NODE_SPACE_CREATE_INFO_MSFT = 1000049000,
+ XR_TYPE_SPATIAL_GRAPH_STATIC_NODE_BINDING_CREATE_INFO_MSFT = 1000049001,
+ XR_TYPE_SPATIAL_GRAPH_NODE_BINDING_PROPERTIES_GET_INFO_MSFT = 1000049002,
+ XR_TYPE_SPATIAL_GRAPH_NODE_BINDING_PROPERTIES_MSFT = 1000049003,
XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT = 1000051000,
XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT = 1000051001,
XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT = 1000051002,
@@ -332,6 +341,7 @@ typedef enum XrStructureType {
XR_TYPE_COMPOSITION_LAYER_REPROJECTION_PLANE_OVERRIDE_MSFT = 1000066001,
XR_TYPE_ANDROID_SURFACE_SWAPCHAIN_CREATE_INFO_FB = 1000070000,
XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB = 1000072000,
+ XR_TYPE_INTERACTION_PROFILE_DPAD_BINDING_EXT = 1000078000,
XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE = 1000079000,
XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT = 1000080000,
XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR = 1000089000,
@@ -371,6 +381,12 @@ typedef enum XrStructureType {
XR_TYPE_HAND_TRACKING_SCALE_FB = 1000110003,
XR_TYPE_HAND_TRACKING_AIM_STATE_FB = 1000111001,
XR_TYPE_HAND_TRACKING_CAPSULES_STATE_FB = 1000112000,
+ XR_TYPE_SYSTEM_SPATIAL_ENTITY_PROPERTIES_FB = 1000113004,
+ XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_FB = 1000113003,
+ XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB = 1000113007,
+ XR_TYPE_SPACE_COMPONENT_STATUS_FB = 1000113001,
+ XR_TYPE_EVENT_DATA_SPATIAL_ANCHOR_CREATE_COMPLETE_FB = 1000113005,
+ XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB = 1000113006,
XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB = 1000114000,
XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB = 1000114001,
XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB = 1000114002,
@@ -388,12 +404,14 @@ typedef enum XrStructureType {
XR_TYPE_PASSTHROUGH_STYLE_FB = 1000118020,
XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_RGBA_FB = 1000118021,
XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_MONO_FB = 1000118022,
+ XR_TYPE_PASSTHROUGH_BRIGHTNESS_CONTRAST_SATURATION_FB = 1000118023,
XR_TYPE_EVENT_DATA_PASSTHROUGH_STATE_CHANGED_FB = 1000118030,
XR_TYPE_RENDER_MODEL_PATH_INFO_FB = 1000119000,
XR_TYPE_RENDER_MODEL_PROPERTIES_FB = 1000119001,
XR_TYPE_RENDER_MODEL_BUFFER_FB = 1000119002,
XR_TYPE_RENDER_MODEL_LOAD_INFO_FB = 1000119003,
XR_TYPE_SYSTEM_RENDER_MODEL_PROPERTIES_FB = 1000119004,
+ XR_TYPE_RENDER_MODEL_CAPABILITIES_REQUEST_FB = 1000119005,
XR_TYPE_BINDING_MODIFICATIONS_KHR = 1000120000,
XR_TYPE_VIEW_LOCATE_FOVEATED_RENDERING_VARJO = 1000121000,
XR_TYPE_FOVEATED_VIEW_CONFIGURATION_VIEW_VARJO = 1000121001,
@@ -404,6 +422,17 @@ typedef enum XrStructureType {
XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO = 1000124002,
XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT = 1000142000,
XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT = 1000142001,
+ XR_TYPE_SPACE_QUERY_INFO_FB = 1000156001,
+ XR_TYPE_SPACE_QUERY_RESULTS_FB = 1000156002,
+ XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB = 1000156003,
+ XR_TYPE_SPACE_UUID_FILTER_INFO_FB = 1000156054,
+ XR_TYPE_SPACE_COMPONENT_FILTER_INFO_FB = 1000156052,
+ XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB = 1000156103,
+ XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB = 1000156104,
+ XR_TYPE_SPACE_SAVE_INFO_FB = 1000158000,
+ XR_TYPE_SPACE_ERASE_INFO_FB = 1000158001,
+ XR_TYPE_EVENT_DATA_SPACE_SAVE_COMPLETE_FB = 1000158106,
+ XR_TYPE_EVENT_DATA_SPACE_ERASE_COMPLETE_FB = 1000158107,
XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB = 1000160000,
XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB = 1000161000,
XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB = 1000162000,
@@ -411,7 +440,12 @@ typedef enum XrStructureType {
XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB = 1000171000,
XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB = 1000171001,
XR_TYPE_DIGITAL_LENS_CONTROL_ALMALENCE = 1000196000,
+ XR_TYPE_SPACE_CONTAINER_FB = 1000199000,
XR_TYPE_PASSTHROUGH_KEYBOARD_HANDS_INTENSITY_FB = 1000203002,
+ XR_TYPE_COMPOSITION_LAYER_SETTINGS_FB = 1000204000,
+ XR_TYPE_VULKAN_SWAPCHAIN_CREATE_INFO_META = 1000227000,
+ XR_TYPE_PERFORMANCE_METRICS_STATE_META = 1000232001,
+ XR_TYPE_PERFORMANCE_METRICS_COUNTER_META = 1000232002,
XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR,
XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR,
XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR,
@@ -487,6 +521,7 @@ typedef enum XrObjectType {
XR_OBJECT_TYPE_ACTION = 6,
XR_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000019000,
XR_OBJECT_TYPE_SPATIAL_ANCHOR_MSFT = 1000039000,
+ XR_OBJECT_TYPE_SPATIAL_GRAPH_NODE_BINDING_MSFT = 1000049000,
XR_OBJECT_TYPE_HAND_TRACKER_EXT = 1000051000,
XR_OBJECT_TYPE_SCENE_OBSERVER_MSFT = 1000097000,
XR_OBJECT_TYPE_SCENE_MSFT = 1000097001,
@@ -1424,7 +1459,7 @@ typedef struct XrCompositionLayerCubeKHR {
#define XR_KHR_composition_layer_depth 1
-#define XR_KHR_composition_layer_depth_SPEC_VERSION 5
+#define XR_KHR_composition_layer_depth_SPEC_VERSION 6
#define XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME "XR_KHR_composition_layer_depth"
// XrCompositionLayerDepthInfoKHR extends XrCompositionLayerProjectionView
typedef struct XrCompositionLayerDepthInfoKHR {
@@ -1766,7 +1801,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSessionInsertDebugUtilsLabelEXT(
#define XR_EXT_eye_gaze_interaction 1
-#define XR_EXT_eye_gaze_interaction_SPEC_VERSION 1
+#define XR_EXT_eye_gaze_interaction_SPEC_VERSION 2
#define XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME "XR_EXT_eye_gaze_interaction"
// XrSystemEyeGazeInteractionPropertiesEXT extends XrSystemProperties
typedef struct XrSystemEyeGazeInteractionPropertiesEXT {
@@ -1977,8 +2012,10 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetInputDeviceLocationEXT(
#define XR_MSFT_spatial_graph_bridge 1
-#define XR_MSFT_spatial_graph_bridge_SPEC_VERSION 1
+XR_DEFINE_HANDLE(XrSpatialGraphNodeBindingMSFT)
+#define XR_MSFT_spatial_graph_bridge_SPEC_VERSION 2
#define XR_MSFT_SPATIAL_GRAPH_BRIDGE_EXTENSION_NAME "XR_MSFT_spatial_graph_bridge"
+#define XR_GUID_SIZE_MSFT 16
typedef enum XrSpatialGraphNodeTypeMSFT {
XR_SPATIAL_GRAPH_NODE_TYPE_STATIC_MSFT = 1,
@@ -1989,11 +2026,34 @@ typedef struct XrSpatialGraphNodeSpaceCreateInfoMSFT {
XrStructureType type;
const void* XR_MAY_ALIAS next;
XrSpatialGraphNodeTypeMSFT nodeType;
- uint8_t nodeId[16];
+ uint8_t nodeId[XR_GUID_SIZE_MSFT];
XrPosef pose;
} XrSpatialGraphNodeSpaceCreateInfoMSFT;
+typedef struct XrSpatialGraphStaticNodeBindingCreateInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrPosef poseInSpace;
+ XrTime time;
+} XrSpatialGraphStaticNodeBindingCreateInfoMSFT;
+
+typedef struct XrSpatialGraphNodeBindingPropertiesGetInfoMSFT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpatialGraphNodeBindingPropertiesGetInfoMSFT;
+
+typedef struct XrSpatialGraphNodeBindingPropertiesMSFT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint8_t nodeId[XR_GUID_SIZE_MSFT];
+ XrPosef poseInNodeSpace;
+} XrSpatialGraphNodeBindingPropertiesMSFT;
+
typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialGraphNodeSpaceMSFT)(XrSession session, const XrSpatialGraphNodeSpaceCreateInfoMSFT* createInfo, XrSpace* space);
+typedef XrResult (XRAPI_PTR *PFN_xrTryCreateSpatialGraphStaticNodeBindingMSFT)(XrSession session, const XrSpatialGraphStaticNodeBindingCreateInfoMSFT* createInfo, XrSpatialGraphNodeBindingMSFT* nodeBinding);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroySpatialGraphNodeBindingMSFT)(XrSpatialGraphNodeBindingMSFT nodeBinding);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialGraphNodeBindingPropertiesMSFT)(XrSpatialGraphNodeBindingMSFT nodeBinding, const XrSpatialGraphNodeBindingPropertiesGetInfoMSFT* getInfo, XrSpatialGraphNodeBindingPropertiesMSFT* properties);
#ifndef XR_NO_PROTOTYPES
#ifdef XR_EXTENSION_PROTOTYPES
@@ -2001,6 +2061,19 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialGraphNodeSpaceMSFT(
XrSession session,
const XrSpatialGraphNodeSpaceCreateInfoMSFT* createInfo,
XrSpace* space);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrTryCreateSpatialGraphStaticNodeBindingMSFT(
+ XrSession session,
+ const XrSpatialGraphStaticNodeBindingCreateInfoMSFT* createInfo,
+ XrSpatialGraphNodeBindingMSFT* nodeBinding);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialGraphNodeBindingMSFT(
+ XrSpatialGraphNodeBindingMSFT nodeBinding);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialGraphNodeBindingPropertiesMSFT(
+ XrSpatialGraphNodeBindingMSFT nodeBinding,
+ const XrSpatialGraphNodeBindingPropertiesGetInfoMSFT* getInfo,
+ XrSpatialGraphNodeBindingPropertiesMSFT* properties);
#endif /* XR_EXTENSION_PROTOTYPES */
#endif /* !XR_NO_PROTOTYPES */
@@ -2056,6 +2129,7 @@ typedef enum XrHandJointEXT {
typedef enum XrHandJointSetEXT {
XR_HAND_JOINT_SET_DEFAULT_EXT = 0,
+ XR_HAND_JOINT_SET_HAND_WITH_FOREARM_ULTRALEAP = 1000149000,
XR_HAND_JOINT_SET_MAX_ENUM_EXT = 0x7FFFFFFF
} XrHandJointSetEXT;
// XrSystemHandTrackingPropertiesEXT extends XrSystemProperties
@@ -2454,6 +2528,25 @@ typedef struct XrCompositionLayerSecureContentFB {
+#define XR_EXT_dpad_binding 1
+#define XR_EXT_dpad_binding_SPEC_VERSION 1
+#define XR_EXT_DPAD_BINDING_EXTENSION_NAME "XR_EXT_dpad_binding"
+typedef struct XrInteractionProfileDpadBindingEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPath binding;
+ XrActionSet actionSet;
+ float forceThreshold;
+ float forceThresholdReleased;
+ float centerRegion;
+ float wedgeAngle;
+ XrBool32 isSticky;
+ const XrHapticBaseHeader* onHaptic;
+ const XrHapticBaseHeader* offHaptic;
+} XrInteractionProfileDpadBindingEXT;
+
+
+
#define XR_VALVE_analog_threshold 1
#define XR_VALVE_analog_threshold_SPEC_VERSION 2
#define XR_VALVE_ANALOG_THRESHOLD_EXTENSION_NAME "XR_VALVE_analog_threshold"
@@ -3058,10 +3151,20 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFacialExpressionsHTC(
#define XR_HTC_vive_focus3_controller_interaction 1
-#define XR_HTC_vive_focus3_controller_interaction_SPEC_VERSION 1
+#define XR_HTC_vive_focus3_controller_interaction_SPEC_VERSION 2
#define XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_focus3_controller_interaction"
+#define XR_HTC_hand_interaction 1
+#define XR_HTC_hand_interaction_SPEC_VERSION 1
+#define XR_HTC_HAND_INTERACTION_EXTENSION_NAME "XR_HTC_hand_interaction"
+
+
+#define XR_HTC_vive_wrist_tracker_interaction 1
+#define XR_HTC_vive_wrist_tracker_interaction_SPEC_VERSION 1
+#define XR_HTC_VIVE_WRIST_TRACKER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_wrist_tracker_interaction"
+
+
#define XR_FB_color_space 1
#define XR_FB_color_space_SPEC_VERSION 2
#define XR_FB_COLOR_SPACE_EXTENSION_NAME "XR_FB_color_space"
@@ -3103,7 +3206,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetColorSpaceFB(
#define XR_FB_hand_tracking_mesh 1
-#define XR_FB_hand_tracking_mesh_SPEC_VERSION 1
+#define XR_FB_hand_tracking_mesh_SPEC_VERSION 2
#define XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME "XR_FB_hand_tracking_mesh"
typedef struct XrVector4sFB {
int16_t x;
@@ -3132,7 +3235,7 @@ typedef struct XrHandTrackingMeshFB {
int16_t* indices;
} XrHandTrackingMeshFB;
-// XrHandTrackingScaleFB extends XrHandJointsLocateInfoEXT
+// XrHandTrackingScaleFB extends XrHandJointLocationsEXT
typedef struct XrHandTrackingScaleFB {
XrStructureType type;
void* XR_MAY_ALIAS next;
@@ -3154,7 +3257,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetHandMeshFB(
#define XR_FB_hand_tracking_aim 1
-#define XR_FB_hand_tracking_aim_SPEC_VERSION 1
+#define XR_FB_hand_tracking_aim_SPEC_VERSION 2
#define XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME "XR_FB_hand_tracking_aim"
typedef XrFlags64 XrHandTrackingAimFlagsFB;
@@ -3169,7 +3272,7 @@ static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_SYSTEM_GESTURE_BIT_FB
static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_DOMINANT_HAND_BIT_FB = 0x00000080;
static const XrHandTrackingAimFlagsFB XR_HAND_TRACKING_AIM_MENU_PRESSED_BIT_FB = 0x00000100;
-// XrHandTrackingAimStateFB extends XrHandJointsLocateInfoEXT
+// XrHandTrackingAimStateFB extends XrHandJointLocationsEXT
typedef struct XrHandTrackingAimStateFB {
XrStructureType type;
void* XR_MAY_ALIAS next;
@@ -3185,26 +3288,128 @@ typedef struct XrHandTrackingAimStateFB {
#define XR_FB_hand_tracking_capsules 1
#define XR_HAND_TRACKING_CAPSULE_POINT_COUNT_FB 2
-#define XR_FB_HAND_TRACKING_CAPSULE_POINT_COUNT XR_HAND_TRACKING_CAPSULE_POINT_COUNT_FB
#define XR_HAND_TRACKING_CAPSULE_COUNT_FB 19
-#define XR_FB_HAND_TRACKING_CAPSULE_COUNT XR_HAND_TRACKING_CAPSULE_COUNT_FB
-#define XR_FB_hand_tracking_capsules_SPEC_VERSION 2
+#define XR_FB_hand_tracking_capsules_SPEC_VERSION 3
#define XR_FB_HAND_TRACKING_CAPSULES_EXTENSION_NAME "XR_FB_hand_tracking_capsules"
+#define XR_FB_HAND_TRACKING_CAPSULE_POINT_COUNT XR_HAND_TRACKING_CAPSULE_POINT_COUNT_FB
+#define XR_FB_HAND_TRACKING_CAPSULE_COUNT XR_HAND_TRACKING_CAPSULE_COUNT_FB
typedef struct XrHandCapsuleFB {
- XrVector3f points[XR_FB_HAND_TRACKING_CAPSULE_POINT_COUNT];
+ XrVector3f points[XR_HAND_TRACKING_CAPSULE_POINT_COUNT_FB];
float radius;
XrHandJointEXT joint;
} XrHandCapsuleFB;
-// XrHandTrackingCapsulesStateFB extends XrHandJointsLocateInfoEXT
+// XrHandTrackingCapsulesStateFB extends XrHandJointLocationsEXT
typedef struct XrHandTrackingCapsulesStateFB {
XrStructureType type;
void* XR_MAY_ALIAS next;
- XrHandCapsuleFB capsules[XR_FB_HAND_TRACKING_CAPSULE_COUNT];
+ XrHandCapsuleFB capsules[XR_HAND_TRACKING_CAPSULE_COUNT_FB];
} XrHandTrackingCapsulesStateFB;
+#define XR_FB_spatial_entity 1
+XR_DEFINE_ATOM(XrAsyncRequestIdFB)
+#define XR_UUID_SIZE_EXT 16
+#define XR_FB_spatial_entity_SPEC_VERSION 1
+#define XR_FB_SPATIAL_ENTITY_EXTENSION_NAME "XR_FB_spatial_entity"
+
+typedef enum XrSpaceComponentTypeFB {
+ XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB = 0,
+ XR_SPACE_COMPONENT_TYPE_STORABLE_FB = 1,
+ XR_SPACE_COMPONENT_TYPE_SPACE_CONTAINER_FB = 7,
+ XR_SPACE_COMPONENT_TYPE_MAX_ENUM_FB = 0x7FFFFFFF
+} XrSpaceComponentTypeFB;
+// XrSystemSpatialEntityPropertiesFB extends XrSystemProperties
+typedef struct XrSystemSpatialEntityPropertiesFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 supportsSpatialEntity;
+} XrSystemSpatialEntityPropertiesFB;
+
+typedef struct XrSpatialAnchorCreateInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrPosef poseInSpace;
+ XrTime time;
+} XrSpatialAnchorCreateInfoFB;
+
+typedef struct XrSpaceComponentStatusSetInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpaceComponentTypeFB componentType;
+ XrBool32 enabled;
+ XrDuration timeout;
+} XrSpaceComponentStatusSetInfoFB;
+
+typedef struct XrSpaceComponentStatusFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 enabled;
+ XrBool32 changePending;
+} XrSpaceComponentStatusFB;
+
+typedef struct XrUuidEXT {
+ uint8_t data[XR_UUID_SIZE_EXT];
+} XrUuidEXT;
+
+typedef struct XrEventDataSpatialAnchorCreateCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+ XrSpace space;
+ XrUuidEXT uuid;
+} XrEventDataSpatialAnchorCreateCompleteFB;
+
+typedef struct XrEventDataSpaceSetStatusCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+ XrSpace space;
+ XrUuidEXT uuid;
+ XrSpaceComponentTypeFB componentType;
+ XrBool32 enabled;
+} XrEventDataSpaceSetStatusCompleteFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorFB)(XrSession session, const XrSpatialAnchorCreateInfoFB* info, XrAsyncRequestIdFB* requestId);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceUuidFB)(XrSpace space, XrUuidEXT* uuid);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateSpaceSupportedComponentsFB)(XrSpace space, uint32_t componentTypeCapacityInput, uint32_t* componentTypeCountOutput, XrSpaceComponentTypeFB* componentTypes);
+typedef XrResult (XRAPI_PTR *PFN_xrSetSpaceComponentStatusFB)(XrSpace space, const XrSpaceComponentStatusSetInfoFB* info, XrAsyncRequestIdFB* requestId);
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceComponentStatusFB)(XrSpace space, XrSpaceComponentTypeFB componentType, XrSpaceComponentStatusFB* status);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorFB(
+ XrSession session,
+ const XrSpatialAnchorCreateInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceUuidFB(
+ XrSpace space,
+ XrUuidEXT* uuid);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateSpaceSupportedComponentsFB(
+ XrSpace space,
+ uint32_t componentTypeCapacityInput,
+ uint32_t* componentTypeCountOutput,
+ XrSpaceComponentTypeFB* componentTypes);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetSpaceComponentStatusFB(
+ XrSpace space,
+ const XrSpaceComponentStatusSetInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceComponentStatusFB(
+ XrSpace space,
+ XrSpaceComponentTypeFB componentType,
+ XrSpaceComponentStatusFB* status);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
#define XR_FB_foveation 1
XR_DEFINE_HANDLE(XrFoveationProfileFB)
#define XR_FB_foveation_SPEC_VERSION 1
@@ -3346,7 +3551,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateKeyboardSpaceFB(
#define XR_FB_triangle_mesh 1
XR_DEFINE_HANDLE(XrTriangleMeshFB)
-#define XR_FB_triangle_mesh_SPEC_VERSION 1
+#define XR_FB_triangle_mesh_SPEC_VERSION 2
#define XR_FB_TRIANGLE_MESH_EXTENSION_NAME "XR_FB_triangle_mesh"
typedef enum XrWindingOrderFB {
@@ -3420,7 +3625,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshEndVertexBufferUpdateFB(
XR_DEFINE_HANDLE(XrPassthroughFB)
XR_DEFINE_HANDLE(XrPassthroughLayerFB)
XR_DEFINE_HANDLE(XrGeometryInstanceFB)
-#define XR_FB_passthrough_SPEC_VERSION 1
+#define XR_FB_passthrough_SPEC_VERSION 2
#define XR_FB_PASSTHROUGH_EXTENSION_NAME "XR_FB_passthrough"
#define XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB 256
@@ -3428,6 +3633,7 @@ typedef enum XrPassthroughLayerPurposeFB {
XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB = 0,
XR_PASSTHROUGH_LAYER_PURPOSE_PROJECTED_FB = 1,
XR_PASSTHROUGH_LAYER_PURPOSE_TRACKED_KEYBOARD_HANDS_FB = 1000203001,
+ XR_PASSTHROUGH_LAYER_PURPOSE_TRACKED_KEYBOARD_MASKED_HANDS_FB = 1000203002,
XR_PASSTHROUGH_LAYER_PURPOSE_MAX_ENUM_FB = 0x7FFFFFFF
} XrPassthroughLayerPurposeFB;
typedef XrFlags64 XrPassthroughFlagsFB;
@@ -3499,18 +3705,29 @@ typedef struct XrPassthroughStyleFB {
XrColor4f edgeColor;
} XrPassthroughStyleFB;
+// XrPassthroughColorMapMonoToRgbaFB extends XrPassthroughStyleFB
typedef struct XrPassthroughColorMapMonoToRgbaFB {
XrStructureType type;
const void* XR_MAY_ALIAS next;
XrColor4f textureColorMap[XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB];
} XrPassthroughColorMapMonoToRgbaFB;
+// XrPassthroughColorMapMonoToMonoFB extends XrPassthroughStyleFB
typedef struct XrPassthroughColorMapMonoToMonoFB {
XrStructureType type;
const void* XR_MAY_ALIAS next;
uint8_t textureColorMap[XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB];
} XrPassthroughColorMapMonoToMonoFB;
+// XrPassthroughBrightnessContrastSaturationFB extends XrPassthroughStyleFB
+typedef struct XrPassthroughBrightnessContrastSaturationFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ float brightness;
+ float contrast;
+ float saturation;
+} XrPassthroughBrightnessContrastSaturationFB;
+
typedef struct XrEventDataPassthroughStateChangedFB {
XrStructureType type;
const void* XR_MAY_ALIAS next;
@@ -3584,12 +3801,14 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGeometryInstanceSetTransformFB(
#define XR_NULL_RENDER_MODEL_KEY_FB 0
XR_DEFINE_ATOM(XrRenderModelKeyFB)
-#define XR_FB_render_model_SPEC_VERSION 1
+#define XR_FB_render_model_SPEC_VERSION 2
#define XR_FB_RENDER_MODEL_EXTENSION_NAME "XR_FB_render_model"
#define XR_MAX_RENDER_MODEL_NAME_SIZE_FB 64
typedef XrFlags64 XrRenderModelFlagsFB;
// Flag bits for XrRenderModelFlagsFB
+static const XrRenderModelFlagsFB XR_RENDER_MODEL_SUPPORTS_GLTF_2_0_SUBSET_1_BIT_FB = 0x00000001;
+static const XrRenderModelFlagsFB XR_RENDER_MODEL_SUPPORTS_GLTF_2_0_SUBSET_2_BIT_FB = 0x00000002;
typedef struct XrRenderModelPathInfoFB {
XrStructureType type;
@@ -3628,6 +3847,13 @@ typedef struct XrSystemRenderModelPropertiesFB {
XrBool32 supportsRenderModelLoading;
} XrSystemRenderModelPropertiesFB;
+// XrRenderModelCapabilitiesRequestFB extends XrSystemProperties
+typedef struct XrRenderModelCapabilitiesRequestFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrRenderModelFlagsFB flags;
+} XrRenderModelCapabilitiesRequestFB;
+
typedef XrResult (XRAPI_PTR *PFN_xrEnumerateRenderModelPathsFB)(XrSession session, uint32_t pathCapacityInput, uint32_t* pathCountOutput, XrRenderModelPathInfoFB* paths);
typedef XrResult (XRAPI_PTR *PFN_xrGetRenderModelPropertiesFB)(XrSession session, XrPath path, XrRenderModelPropertiesFB* properties);
typedef XrResult (XRAPI_PTR *PFN_xrLoadRenderModelFB)(XrSession session, const XrRenderModelLoadInfoFB* info, XrRenderModelBufferFB* buffer);
@@ -3767,6 +3993,20 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateMarkerSpaceVARJO(
#endif /* !XR_NO_PROTOTYPES */
+#define XR_VARJO_view_offset 1
+#define XR_VARJO_view_offset_SPEC_VERSION 1
+#define XR_VARJO_VIEW_OFFSET_EXTENSION_NAME "XR_VARJO_view_offset"
+typedef XrResult (XRAPI_PTR *PFN_xrSetViewOffsetVARJO)(XrSession session, float offset);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSetViewOffsetVARJO(
+ XrSession session,
+ float offset);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
#define XR_MSFT_spatial_anchor_persistence 1
XR_DEFINE_HANDLE(XrSpatialAnchorStoreConnectionMSFT)
#define XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_MSFT 256
@@ -3832,12 +4072,212 @@ XRAPI_ATTR XrResult XRAPI_CALL xrClearSpatialAnchorStoreMSFT(
#endif /* !XR_NO_PROTOTYPES */
+#define XR_ULTRALEAP_hand_tracking_forearm 1
+
+#define XR_HAND_FOREARM_JOINT_COUNT_ULTRALEAP 27
+
+#define XR_ULTRALEAP_hand_tracking_forearm_SPEC_VERSION 1
+#define XR_ULTRALEAP_HAND_TRACKING_FOREARM_EXTENSION_NAME "XR_ULTRALEAP_hand_tracking_forearm"
+
+typedef enum XrHandForearmJointULTRALEAP {
+ XR_HAND_FOREARM_JOINT_PALM_ULTRALEAP = 0,
+ XR_HAND_FOREARM_JOINT_WRIST_ULTRALEAP = 1,
+ XR_HAND_FOREARM_JOINT_THUMB_METACARPAL_ULTRALEAP = 2,
+ XR_HAND_FOREARM_JOINT_THUMB_PROXIMAL_ULTRALEAP = 3,
+ XR_HAND_FOREARM_JOINT_THUMB_DISTAL_ULTRALEAP = 4,
+ XR_HAND_FOREARM_JOINT_THUMB_TIP_ULTRALEAP = 5,
+ XR_HAND_FOREARM_JOINT_INDEX_METACARPAL_ULTRALEAP = 6,
+ XR_HAND_FOREARM_JOINT_INDEX_PROXIMAL_ULTRALEAP = 7,
+ XR_HAND_FOREARM_JOINT_INDEX_INTERMEDIATE_ULTRALEAP = 8,
+ XR_HAND_FOREARM_JOINT_INDEX_DISTAL_ULTRALEAP = 9,
+ XR_HAND_FOREARM_JOINT_INDEX_TIP_ULTRALEAP = 10,
+ XR_HAND_FOREARM_JOINT_MIDDLE_METACARPAL_ULTRALEAP = 11,
+ XR_HAND_FOREARM_JOINT_MIDDLE_PROXIMAL_ULTRALEAP = 12,
+ XR_HAND_FOREARM_JOINT_MIDDLE_INTERMEDIATE_ULTRALEAP = 13,
+ XR_HAND_FOREARM_JOINT_MIDDLE_DISTAL_ULTRALEAP = 14,
+ XR_HAND_FOREARM_JOINT_MIDDLE_TIP_ULTRALEAP = 15,
+ XR_HAND_FOREARM_JOINT_RING_METACARPAL_ULTRALEAP = 16,
+ XR_HAND_FOREARM_JOINT_RING_PROXIMAL_ULTRALEAP = 17,
+ XR_HAND_FOREARM_JOINT_RING_INTERMEDIATE_ULTRALEAP = 18,
+ XR_HAND_FOREARM_JOINT_RING_DISTAL_ULTRALEAP = 19,
+ XR_HAND_FOREARM_JOINT_RING_TIP_ULTRALEAP = 20,
+ XR_HAND_FOREARM_JOINT_LITTLE_METACARPAL_ULTRALEAP = 21,
+ XR_HAND_FOREARM_JOINT_LITTLE_PROXIMAL_ULTRALEAP = 22,
+ XR_HAND_FOREARM_JOINT_LITTLE_INTERMEDIATE_ULTRALEAP = 23,
+ XR_HAND_FOREARM_JOINT_LITTLE_DISTAL_ULTRALEAP = 24,
+ XR_HAND_FOREARM_JOINT_LITTLE_TIP_ULTRALEAP = 25,
+ XR_HAND_FOREARM_JOINT_ELBOW_ULTRALEAP = 26,
+ XR_HAND_FOREARM_JOINT_MAX_ENUM_ULTRALEAP = 0x7FFFFFFF
+} XrHandForearmJointULTRALEAP;
+
+
+#define XR_FB_spatial_entity_query 1
+#define XR_FB_spatial_entity_query_SPEC_VERSION 1
+#define XR_FB_SPATIAL_ENTITY_QUERY_EXTENSION_NAME "XR_FB_spatial_entity_query"
+
+typedef enum XrSpaceQueryActionFB {
+ XR_SPACE_QUERY_ACTION_LOAD_FB = 0,
+ XR_SPACE_QUERY_ACTION_MAX_ENUM_FB = 0x7FFFFFFF
+} XrSpaceQueryActionFB;
+
+typedef enum XrSpaceStorageLocationFB {
+ XR_SPACE_STORAGE_LOCATION_INVALID_FB = 0,
+ XR_SPACE_STORAGE_LOCATION_LOCAL_FB = 1,
+ XR_SPACE_STORAGE_LOCATION_MAX_ENUM_FB = 0x7FFFFFFF
+} XrSpaceStorageLocationFB;
+typedef struct XR_MAY_ALIAS XrSpaceQueryInfoBaseHeaderFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpaceQueryInfoBaseHeaderFB;
+
+typedef struct XR_MAY_ALIAS XrSpaceFilterInfoBaseHeaderFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+} XrSpaceFilterInfoBaseHeaderFB;
+
+typedef struct XrSpaceQueryInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpaceQueryActionFB queryAction;
+ uint32_t maxResultCount;
+ XrDuration timeout;
+ const XrSpaceFilterInfoBaseHeaderFB* filter;
+ const XrSpaceFilterInfoBaseHeaderFB* excludeFilter;
+} XrSpaceQueryInfoFB;
+
+// XrSpaceStorageLocationFilterInfoFB extends XrSpaceFilterInfoBaseHeaderFB
+typedef struct XrSpaceStorageLocationFilterInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpaceStorageLocationFB location;
+} XrSpaceStorageLocationFilterInfoFB;
+
+typedef struct XrSpaceUuidFilterInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t uuidCount;
+ XrUuidEXT* uuids;
+} XrSpaceUuidFilterInfoFB;
+
+typedef struct XrSpaceComponentFilterInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpaceComponentTypeFB componentType;
+} XrSpaceComponentFilterInfoFB;
+
+typedef struct XrSpaceQueryResultFB {
+ XrSpace space;
+ XrUuidEXT uuid;
+} XrSpaceQueryResultFB;
+
+typedef struct XrSpaceQueryResultsFB {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t resultCapacityInput;
+ uint32_t resultCountOutput;
+ XrSpaceQueryResultFB* results;
+} XrSpaceQueryResultsFB;
+
+typedef struct XrEventDataSpaceQueryResultsAvailableFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+} XrEventDataSpaceQueryResultsAvailableFB;
+
+typedef struct XrEventDataSpaceQueryCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+} XrEventDataSpaceQueryCompleteFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrQuerySpacesFB)(XrSession session, const XrSpaceQueryInfoBaseHeaderFB* info, XrAsyncRequestIdFB* requestId);
+typedef XrResult (XRAPI_PTR *PFN_xrRetrieveSpaceQueryResultsFB)(XrSession session, XrAsyncRequestIdFB requestId, XrSpaceQueryResultsFB* results);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrQuerySpacesFB(
+ XrSession session,
+ const XrSpaceQueryInfoBaseHeaderFB* info,
+ XrAsyncRequestIdFB* requestId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrRetrieveSpaceQueryResultsFB(
+ XrSession session,
+ XrAsyncRequestIdFB requestId,
+ XrSpaceQueryResultsFB* results);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+#define XR_FB_spatial_entity_storage 1
+#define XR_FB_spatial_entity_storage_SPEC_VERSION 1
+#define XR_FB_SPATIAL_ENTITY_STORAGE_EXTENSION_NAME "XR_FB_spatial_entity_storage"
+
+typedef enum XrSpacePersistenceModeFB {
+ XR_SPACE_PERSISTENCE_MODE_INVALID_FB = 0,
+ XR_SPACE_PERSISTENCE_MODE_INDEFINITE_FB = 1,
+ XR_SPACE_PERSISTENCE_MODE_MAX_ENUM_FB = 0x7FFFFFFF
+} XrSpacePersistenceModeFB;
+typedef struct XrSpaceSaveInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrSpaceStorageLocationFB location;
+ XrSpacePersistenceModeFB persistenceMode;
+} XrSpaceSaveInfoFB;
+
+typedef struct XrSpaceEraseInfoFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrSpaceStorageLocationFB location;
+} XrSpaceEraseInfoFB;
+
+typedef struct XrEventDataSpaceSaveCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+ XrSpace space;
+ XrUuidEXT uuid;
+ XrSpaceStorageLocationFB location;
+} XrEventDataSpaceSaveCompleteFB;
+
+typedef struct XrEventDataSpaceEraseCompleteFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrAsyncRequestIdFB requestId;
+ XrResult result;
+ XrSpace space;
+ XrUuidEXT uuid;
+ XrSpaceStorageLocationFB location;
+} XrEventDataSpaceEraseCompleteFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrSaveSpaceFB)(XrSession session, const XrSpaceSaveInfoFB* info, XrAsyncRequestIdFB* requestId);
+typedef XrResult (XRAPI_PTR *PFN_xrEraseSpaceFB)(XrSession session, const XrSpaceEraseInfoFB* info, XrAsyncRequestIdFB* requestId);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrSaveSpaceFB(
+ XrSession session,
+ const XrSpaceSaveInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEraseSpaceFB(
+ XrSession session,
+ const XrSpaceEraseInfoFB* info,
+ XrAsyncRequestIdFB* requestId);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
#define XR_FB_space_warp 1
-#define XR_FB_space_warp_SPEC_VERSION 1
+#define XR_FB_space_warp_SPEC_VERSION 2
#define XR_FB_SPACE_WARP_EXTENSION_NAME "XR_FB_space_warp"
typedef XrFlags64 XrCompositionLayerSpaceWarpInfoFlagsFB;
// Flag bits for XrCompositionLayerSpaceWarpInfoFlagsFB
+static const XrCompositionLayerSpaceWarpInfoFlagsFB XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB = 0x00000001;
// XrCompositionLayerSpaceWarpInfoFB extends XrCompositionLayerProjectionView
typedef struct XrCompositionLayerSpaceWarpInfoFB {
@@ -3888,8 +4328,31 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetDigitalLensControlALMALENCE(
#endif /* !XR_NO_PROTOTYPES */
+#define XR_FB_spatial_entity_container 1
+#define XR_FB_spatial_entity_container_SPEC_VERSION 1
+#define XR_FB_SPATIAL_ENTITY_CONTAINER_EXTENSION_NAME "XR_FB_spatial_entity_container"
+typedef struct XrSpaceContainerFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t uuidCapacityInput;
+ uint32_t* uuidCountOutput;
+ XrUuidEXT* uuids;
+} XrSpaceContainerFB;
+
+typedef XrResult (XRAPI_PTR *PFN_xrGetSpaceContainerFB)(XrSession session, XrSpace space, XrSpaceContainerFB* spaceContainerOutput);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceContainerFB(
+ XrSession session,
+ XrSpace space,
+ XrSpaceContainerFB* spaceContainerOutput);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
#define XR_FB_passthrough_keyboard_hands 1
-#define XR_FB_passthrough_keyboard_hands_SPEC_VERSION 1
+#define XR_FB_passthrough_keyboard_hands_SPEC_VERSION 2
#define XR_FB_PASSTHROUGH_KEYBOARD_HANDS_EXTENSION_NAME "XR_FB_passthrough_keyboard_hands"
typedef struct XrPassthroughKeyboardHandsIntensityFB {
XrStructureType type;
@@ -3909,14 +4372,92 @@ XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughLayerSetKeyboardHandsIntensityFB(
#endif /* !XR_NO_PROTOTYPES */
+#define XR_FB_composition_layer_settings 1
+#define XR_FB_composition_layer_settings_SPEC_VERSION 1
+#define XR_FB_COMPOSITION_LAYER_SETTINGS_EXTENSION_NAME "XR_FB_composition_layer_settings"
+typedef XrFlags64 XrCompositionLayerSettingsFlagsFB;
+
+// Flag bits for XrCompositionLayerSettingsFlagsFB
+static const XrCompositionLayerSettingsFlagsFB XR_COMPOSITION_LAYER_SETTINGS_NORMAL_SUPER_SAMPLING_BIT_FB = 0x00000001;
+static const XrCompositionLayerSettingsFlagsFB XR_COMPOSITION_LAYER_SETTINGS_QUALITY_SUPER_SAMPLING_BIT_FB = 0x00000002;
+static const XrCompositionLayerSettingsFlagsFB XR_COMPOSITION_LAYER_SETTINGS_NORMAL_SHARPENING_BIT_FB = 0x00000004;
+static const XrCompositionLayerSettingsFlagsFB XR_COMPOSITION_LAYER_SETTINGS_QUALITY_SHARPENING_BIT_FB = 0x00000008;
+
+// XrCompositionLayerSettingsFB extends XrCompositionLayerBaseHeader
+typedef struct XrCompositionLayerSettingsFB {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrCompositionLayerSettingsFlagsFB layerFlags;
+} XrCompositionLayerSettingsFB;
+
+
+
+#define XR_META_performance_metrics 1
+#define XR_META_performance_metrics_SPEC_VERSION 1
+#define XR_META_PERFORMANCE_METRICS_EXTENSION_NAME "XR_META_performance_metrics"
+
+typedef enum XrPerformanceMetricsCounterUnitMETA {
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_GENERIC_META = 0,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_PERCENTAGE_META = 1,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META = 2,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_BYTES_META = 3,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_HERTZ_META = 4,
+ XR_PERFORMANCE_METRICS_COUNTER_UNIT_MAX_ENUM_META = 0x7FFFFFFF
+} XrPerformanceMetricsCounterUnitMETA;
+typedef XrFlags64 XrPerformanceMetricsCounterFlagsMETA;
+
+// Flag bits for XrPerformanceMetricsCounterFlagsMETA
+static const XrPerformanceMetricsCounterFlagsMETA XR_PERFORMANCE_METRICS_COUNTER_ANY_VALUE_VALID_BIT_META = 0x00000001;
+static const XrPerformanceMetricsCounterFlagsMETA XR_PERFORMANCE_METRICS_COUNTER_UINT_VALUE_VALID_BIT_META = 0x00000002;
+static const XrPerformanceMetricsCounterFlagsMETA XR_PERFORMANCE_METRICS_COUNTER_FLOAT_VALUE_VALID_BIT_META = 0x00000004;
+
+typedef struct XrPerformanceMetricsStateMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 enabled;
+} XrPerformanceMetricsStateMETA;
+
+typedef struct XrPerformanceMetricsCounterMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrPerformanceMetricsCounterFlagsMETA counterFlags;
+ XrPerformanceMetricsCounterUnitMETA counterUnit;
+ uint32_t uintValue;
+ float floatValue;
+} XrPerformanceMetricsCounterMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrEnumeratePerformanceMetricsCounterPathsMETA)(XrInstance instance, uint32_t counterPathCapacityInput, uint32_t* counterPathCountOutput, XrPath* counterPaths);
+typedef XrResult (XRAPI_PTR *PFN_xrSetPerformanceMetricsStateMETA)(XrSession session, const XrPerformanceMetricsStateMETA* state);
+typedef XrResult (XRAPI_PTR *PFN_xrGetPerformanceMetricsStateMETA)(XrSession session, XrPerformanceMetricsStateMETA* state);
+typedef XrResult (XRAPI_PTR *PFN_xrQueryPerformanceMetricsCounterMETA)(XrSession session, XrPath counterPath, XrPerformanceMetricsCounterMETA* counter);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumeratePerformanceMetricsCounterPathsMETA(
+ XrInstance instance,
+ uint32_t counterPathCapacityInput,
+ uint32_t* counterPathCountOutput,
+ XrPath* counterPaths);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetPerformanceMetricsStateMETA(
+ XrSession session,
+ const XrPerformanceMetricsStateMETA* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetPerformanceMetricsStateMETA(
+ XrSession session,
+ XrPerformanceMetricsStateMETA* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrQueryPerformanceMetricsCounterMETA(
+ XrSession session,
+ XrPath counterPath,
+ XrPerformanceMetricsCounterMETA* counter);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
#define XR_EXT_uuid 1
#define XR_EXT_uuid_SPEC_VERSION 1
#define XR_EXT_UUID_EXTENSION_NAME "XR_EXT_uuid"
-#define XR_UUID_SIZE_EXT 16
-typedef struct XrUuidEXT {
- uint8_t data[XR_UUID_SIZE_EXT];
-} XrUuidEXT;
-
#ifdef __cplusplus
}
diff --git a/thirdparty/openxr/include/openxr/openxr_platform.h b/thirdparty/openxr/include/openxr/openxr_platform.h
index eb5e5f8c4b..f0fbf6955a 100644
--- a/thirdparty/openxr/include/openxr/openxr_platform.h
+++ b/thirdparty/openxr/include/openxr/openxr_platform.h
@@ -275,7 +275,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirementsKHR(
#ifdef XR_USE_GRAPHICS_API_D3D11
#define XR_KHR_D3D11_enable 1
-#define XR_KHR_D3D11_enable_SPEC_VERSION 8
+#define XR_KHR_D3D11_enable_SPEC_VERSION 9
#define XR_KHR_D3D11_ENABLE_EXTENSION_NAME "XR_KHR_D3D11_enable"
// XrGraphicsBindingD3D11KHR extends XrSessionCreateInfo
typedef struct XrGraphicsBindingD3D11KHR {
@@ -312,7 +312,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D11GraphicsRequirementsKHR(
#ifdef XR_USE_GRAPHICS_API_D3D12
#define XR_KHR_D3D12_enable 1
-#define XR_KHR_D3D12_enable_SPEC_VERSION 8
+#define XR_KHR_D3D12_enable_SPEC_VERSION 9
#define XR_KHR_D3D12_ENABLE_EXTENSION_NAME "XR_KHR_D3D12_enable"
// XrGraphicsBindingD3D12KHR extends XrSessionCreateInfo
typedef struct XrGraphicsBindingD3D12KHR {
@@ -668,6 +668,21 @@ typedef struct XrSwapchainStateSamplerVulkanFB {
#endif /* XR_USE_GRAPHICS_API_VULKAN */
+#ifdef XR_USE_GRAPHICS_API_VULKAN
+
+#define XR_META_vulkan_swapchain_create_info 1
+#define XR_META_vulkan_swapchain_create_info_SPEC_VERSION 1
+#define XR_META_VULKAN_SWAPCHAIN_CREATE_INFO_EXTENSION_NAME "XR_META_vulkan_swapchain_create_info"
+// XrVulkanSwapchainCreateInfoMETA extends XrSwapchainCreateInfo
+typedef struct XrVulkanSwapchainCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ VkImageCreateFlags additionalCreateFlags;
+ VkImageUsageFlags additionalUsageFlags;
+} XrVulkanSwapchainCreateInfoMETA;
+
+#endif /* XR_USE_GRAPHICS_API_VULKAN */
+
#ifdef __cplusplus
}
#endif
diff --git a/thirdparty/openxr/include/openxr/openxr_reflection.h b/thirdparty/openxr/include/openxr/openxr_reflection.h
index 2bc14be600..163b54e4e4 100644
--- a/thirdparty/openxr/include/openxr/openxr_reflection.h
+++ b/thirdparty/openxr/include/openxr/openxr_reflection.h
@@ -100,6 +100,10 @@ XR_ENUM_STR(XrResult);
_(XR_ERROR_SCENE_COMPUTE_CONSISTENCY_MISMATCH_MSFT, -1000097005) \
_(XR_ERROR_DISPLAY_REFRESH_RATE_UNSUPPORTED_FB, -1000101000) \
_(XR_ERROR_COLOR_SPACE_UNSUPPORTED_FB, -1000108000) \
+ _(XR_ERROR_SPACE_COMPONENT_NOT_SUPPORTED_FB, -1000113000) \
+ _(XR_ERROR_SPACE_COMPONENT_NOT_ENABLED_FB, -1000113001) \
+ _(XR_ERROR_SPACE_COMPONENT_STATUS_PENDING_FB, -1000113002) \
+ _(XR_ERROR_SPACE_COMPONENT_STATUS_ALREADY_SET_FB, -1000113003) \
_(XR_ERROR_UNEXPECTED_STATE_PASSTHROUGH_FB, -1000118000) \
_(XR_ERROR_FEATURE_ALREADY_CREATED_PASSTHROUGH_FB, -1000118001) \
_(XR_ERROR_FEATURE_REQUIRED_PASSTHROUGH_FB, -1000118002) \
@@ -208,6 +212,9 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_VIEW_CONFIGURATION_DEPTH_RANGE_EXT, 1000046000) \
_(XR_TYPE_GRAPHICS_BINDING_EGL_MNDX, 1000048004) \
_(XR_TYPE_SPATIAL_GRAPH_NODE_SPACE_CREATE_INFO_MSFT, 1000049000) \
+ _(XR_TYPE_SPATIAL_GRAPH_STATIC_NODE_BINDING_CREATE_INFO_MSFT, 1000049001) \
+ _(XR_TYPE_SPATIAL_GRAPH_NODE_BINDING_PROPERTIES_GET_INFO_MSFT, 1000049002) \
+ _(XR_TYPE_SPATIAL_GRAPH_NODE_BINDING_PROPERTIES_MSFT, 1000049003) \
_(XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT, 1000051000) \
_(XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT, 1000051001) \
_(XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT, 1000051002) \
@@ -235,6 +242,7 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_COMPOSITION_LAYER_REPROJECTION_PLANE_OVERRIDE_MSFT, 1000066001) \
_(XR_TYPE_ANDROID_SURFACE_SWAPCHAIN_CREATE_INFO_FB, 1000070000) \
_(XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB, 1000072000) \
+ _(XR_TYPE_INTERACTION_PROFILE_DPAD_BINDING_EXT, 1000078000) \
_(XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE, 1000079000) \
_(XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT, 1000080000) \
_(XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR, 1000089000) \
@@ -274,6 +282,12 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_HAND_TRACKING_SCALE_FB, 1000110003) \
_(XR_TYPE_HAND_TRACKING_AIM_STATE_FB, 1000111001) \
_(XR_TYPE_HAND_TRACKING_CAPSULES_STATE_FB, 1000112000) \
+ _(XR_TYPE_SYSTEM_SPATIAL_ENTITY_PROPERTIES_FB, 1000113004) \
+ _(XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_FB, 1000113003) \
+ _(XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB, 1000113007) \
+ _(XR_TYPE_SPACE_COMPONENT_STATUS_FB, 1000113001) \
+ _(XR_TYPE_EVENT_DATA_SPATIAL_ANCHOR_CREATE_COMPLETE_FB, 1000113005) \
+ _(XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB, 1000113006) \
_(XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB, 1000114000) \
_(XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB, 1000114001) \
_(XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB, 1000114002) \
@@ -291,12 +305,14 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_PASSTHROUGH_STYLE_FB, 1000118020) \
_(XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_RGBA_FB, 1000118021) \
_(XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_MONO_FB, 1000118022) \
+ _(XR_TYPE_PASSTHROUGH_BRIGHTNESS_CONTRAST_SATURATION_FB, 1000118023) \
_(XR_TYPE_EVENT_DATA_PASSTHROUGH_STATE_CHANGED_FB, 1000118030) \
_(XR_TYPE_RENDER_MODEL_PATH_INFO_FB, 1000119000) \
_(XR_TYPE_RENDER_MODEL_PROPERTIES_FB, 1000119001) \
_(XR_TYPE_RENDER_MODEL_BUFFER_FB, 1000119002) \
_(XR_TYPE_RENDER_MODEL_LOAD_INFO_FB, 1000119003) \
_(XR_TYPE_SYSTEM_RENDER_MODEL_PROPERTIES_FB, 1000119004) \
+ _(XR_TYPE_RENDER_MODEL_CAPABILITIES_REQUEST_FB, 1000119005) \
_(XR_TYPE_BINDING_MODIFICATIONS_KHR, 1000120000) \
_(XR_TYPE_VIEW_LOCATE_FOVEATED_RENDERING_VARJO, 1000121000) \
_(XR_TYPE_FOVEATED_VIEW_CONFIGURATION_VIEW_VARJO, 1000121001) \
@@ -307,6 +323,17 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO, 1000124002) \
_(XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT, 1000142000) \
_(XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT, 1000142001) \
+ _(XR_TYPE_SPACE_QUERY_INFO_FB, 1000156001) \
+ _(XR_TYPE_SPACE_QUERY_RESULTS_FB, 1000156002) \
+ _(XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB, 1000156003) \
+ _(XR_TYPE_SPACE_UUID_FILTER_INFO_FB, 1000156054) \
+ _(XR_TYPE_SPACE_COMPONENT_FILTER_INFO_FB, 1000156052) \
+ _(XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB, 1000156103) \
+ _(XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB, 1000156104) \
+ _(XR_TYPE_SPACE_SAVE_INFO_FB, 1000158000) \
+ _(XR_TYPE_SPACE_ERASE_INFO_FB, 1000158001) \
+ _(XR_TYPE_EVENT_DATA_SPACE_SAVE_COMPLETE_FB, 1000158106) \
+ _(XR_TYPE_EVENT_DATA_SPACE_ERASE_COMPLETE_FB, 1000158107) \
_(XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB, 1000160000) \
_(XR_TYPE_SWAPCHAIN_STATE_ANDROID_SURFACE_DIMENSIONS_FB, 1000161000) \
_(XR_TYPE_SWAPCHAIN_STATE_SAMPLER_OPENGL_ES_FB, 1000162000) \
@@ -314,7 +341,12 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB, 1000171000) \
_(XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB, 1000171001) \
_(XR_TYPE_DIGITAL_LENS_CONTROL_ALMALENCE, 1000196000) \
+ _(XR_TYPE_SPACE_CONTAINER_FB, 1000199000) \
_(XR_TYPE_PASSTHROUGH_KEYBOARD_HANDS_INTENSITY_FB, 1000203002) \
+ _(XR_TYPE_COMPOSITION_LAYER_SETTINGS_FB, 1000204000) \
+ _(XR_TYPE_VULKAN_SWAPCHAIN_CREATE_INFO_META, 1000227000) \
+ _(XR_TYPE_PERFORMANCE_METRICS_STATE_META, 1000232001) \
+ _(XR_TYPE_PERFORMANCE_METRICS_COUNTER_META, 1000232002) \
_(XR_STRUCTURE_TYPE_MAX_ENUM, 0x7FFFFFFF)
#define XR_LIST_ENUM_XrFormFactor(_) \
@@ -379,6 +411,7 @@ XR_ENUM_STR(XrResult);
_(XR_OBJECT_TYPE_ACTION, 6) \
_(XR_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT, 1000019000) \
_(XR_OBJECT_TYPE_SPATIAL_ANCHOR_MSFT, 1000039000) \
+ _(XR_OBJECT_TYPE_SPATIAL_GRAPH_NODE_BINDING_MSFT, 1000049000) \
_(XR_OBJECT_TYPE_HAND_TRACKER_EXT, 1000051000) \
_(XR_OBJECT_TYPE_SCENE_OBSERVER_MSFT, 1000097000) \
_(XR_OBJECT_TYPE_SCENE_MSFT, 1000097001) \
@@ -478,6 +511,7 @@ XR_ENUM_STR(XrResult);
#define XR_LIST_ENUM_XrHandJointSetEXT(_) \
_(XR_HAND_JOINT_SET_DEFAULT_EXT, 0) \
+ _(XR_HAND_JOINT_SET_HAND_WITH_FOREARM_ULTRALEAP, 1000149000) \
_(XR_HAND_JOINT_SET_MAX_ENUM_EXT, 0x7FFFFFFF)
#define XR_LIST_ENUM_XrHandPoseTypeMSFT(_) \
@@ -623,6 +657,12 @@ XR_ENUM_STR(XrResult);
_(XR_COLOR_SPACE_ADOBE_RGB_FB, 7) \
_(XR_COLOR_SPACE_MAX_ENUM_FB, 0x7FFFFFFF)
+#define XR_LIST_ENUM_XrSpaceComponentTypeFB(_) \
+ _(XR_SPACE_COMPONENT_TYPE_LOCATABLE_FB, 0) \
+ _(XR_SPACE_COMPONENT_TYPE_STORABLE_FB, 1) \
+ _(XR_SPACE_COMPONENT_TYPE_SPACE_CONTAINER_FB, 7) \
+ _(XR_SPACE_COMPONENT_TYPE_MAX_ENUM_FB, 0x7FFFFFFF)
+
#define XR_LIST_ENUM_XrFoveationLevelFB(_) \
_(XR_FOVEATION_LEVEL_NONE_FB, 0) \
_(XR_FOVEATION_LEVEL_LOW_FB, 1) \
@@ -645,8 +685,61 @@ XR_ENUM_STR(XrResult);
_(XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB, 0) \
_(XR_PASSTHROUGH_LAYER_PURPOSE_PROJECTED_FB, 1) \
_(XR_PASSTHROUGH_LAYER_PURPOSE_TRACKED_KEYBOARD_HANDS_FB, 1000203001) \
+ _(XR_PASSTHROUGH_LAYER_PURPOSE_TRACKED_KEYBOARD_MASKED_HANDS_FB, 1000203002) \
_(XR_PASSTHROUGH_LAYER_PURPOSE_MAX_ENUM_FB, 0x7FFFFFFF)
+#define XR_LIST_ENUM_XrHandForearmJointULTRALEAP(_) \
+ _(XR_HAND_FOREARM_JOINT_PALM_ULTRALEAP, 0) \
+ _(XR_HAND_FOREARM_JOINT_WRIST_ULTRALEAP, 1) \
+ _(XR_HAND_FOREARM_JOINT_THUMB_METACARPAL_ULTRALEAP, 2) \
+ _(XR_HAND_FOREARM_JOINT_THUMB_PROXIMAL_ULTRALEAP, 3) \
+ _(XR_HAND_FOREARM_JOINT_THUMB_DISTAL_ULTRALEAP, 4) \
+ _(XR_HAND_FOREARM_JOINT_THUMB_TIP_ULTRALEAP, 5) \
+ _(XR_HAND_FOREARM_JOINT_INDEX_METACARPAL_ULTRALEAP, 6) \
+ _(XR_HAND_FOREARM_JOINT_INDEX_PROXIMAL_ULTRALEAP, 7) \
+ _(XR_HAND_FOREARM_JOINT_INDEX_INTERMEDIATE_ULTRALEAP, 8) \
+ _(XR_HAND_FOREARM_JOINT_INDEX_DISTAL_ULTRALEAP, 9) \
+ _(XR_HAND_FOREARM_JOINT_INDEX_TIP_ULTRALEAP, 10) \
+ _(XR_HAND_FOREARM_JOINT_MIDDLE_METACARPAL_ULTRALEAP, 11) \
+ _(XR_HAND_FOREARM_JOINT_MIDDLE_PROXIMAL_ULTRALEAP, 12) \
+ _(XR_HAND_FOREARM_JOINT_MIDDLE_INTERMEDIATE_ULTRALEAP, 13) \
+ _(XR_HAND_FOREARM_JOINT_MIDDLE_DISTAL_ULTRALEAP, 14) \
+ _(XR_HAND_FOREARM_JOINT_MIDDLE_TIP_ULTRALEAP, 15) \
+ _(XR_HAND_FOREARM_JOINT_RING_METACARPAL_ULTRALEAP, 16) \
+ _(XR_HAND_FOREARM_JOINT_RING_PROXIMAL_ULTRALEAP, 17) \
+ _(XR_HAND_FOREARM_JOINT_RING_INTERMEDIATE_ULTRALEAP, 18) \
+ _(XR_HAND_FOREARM_JOINT_RING_DISTAL_ULTRALEAP, 19) \
+ _(XR_HAND_FOREARM_JOINT_RING_TIP_ULTRALEAP, 20) \
+ _(XR_HAND_FOREARM_JOINT_LITTLE_METACARPAL_ULTRALEAP, 21) \
+ _(XR_HAND_FOREARM_JOINT_LITTLE_PROXIMAL_ULTRALEAP, 22) \
+ _(XR_HAND_FOREARM_JOINT_LITTLE_INTERMEDIATE_ULTRALEAP, 23) \
+ _(XR_HAND_FOREARM_JOINT_LITTLE_DISTAL_ULTRALEAP, 24) \
+ _(XR_HAND_FOREARM_JOINT_LITTLE_TIP_ULTRALEAP, 25) \
+ _(XR_HAND_FOREARM_JOINT_ELBOW_ULTRALEAP, 26) \
+ _(XR_HAND_FOREARM_JOINT_MAX_ENUM_ULTRALEAP, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrSpaceQueryActionFB(_) \
+ _(XR_SPACE_QUERY_ACTION_LOAD_FB, 0) \
+ _(XR_SPACE_QUERY_ACTION_MAX_ENUM_FB, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrSpaceStorageLocationFB(_) \
+ _(XR_SPACE_STORAGE_LOCATION_INVALID_FB, 0) \
+ _(XR_SPACE_STORAGE_LOCATION_LOCAL_FB, 1) \
+ _(XR_SPACE_STORAGE_LOCATION_MAX_ENUM_FB, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrSpacePersistenceModeFB(_) \
+ _(XR_SPACE_PERSISTENCE_MODE_INVALID_FB, 0) \
+ _(XR_SPACE_PERSISTENCE_MODE_INDEFINITE_FB, 1) \
+ _(XR_SPACE_PERSISTENCE_MODE_MAX_ENUM_FB, 0x7FFFFFFF)
+
+#define XR_LIST_ENUM_XrPerformanceMetricsCounterUnitMETA(_) \
+ _(XR_PERFORMANCE_METRICS_COUNTER_UNIT_GENERIC_META, 0) \
+ _(XR_PERFORMANCE_METRICS_COUNTER_UNIT_PERCENTAGE_META, 1) \
+ _(XR_PERFORMANCE_METRICS_COUNTER_UNIT_MILLISECONDS_META, 2) \
+ _(XR_PERFORMANCE_METRICS_COUNTER_UNIT_BYTES_META, 3) \
+ _(XR_PERFORMANCE_METRICS_COUNTER_UNIT_HERTZ_META, 4) \
+ _(XR_PERFORMANCE_METRICS_COUNTER_UNIT_MAX_ENUM_META, 0x7FFFFFFF)
+
#define XR_LIST_BITS_XrInstanceCreateFlags(_)
#define XR_LIST_BITS_XrSessionCreateFlags(_)
@@ -763,13 +856,27 @@ XR_ENUM_STR(XrResult);
_(XR_PASSTHROUGH_STATE_CHANGED_RECOVERABLE_ERROR_BIT_FB, 0x00000004) \
_(XR_PASSTHROUGH_STATE_CHANGED_RESTORED_ERROR_BIT_FB, 0x00000008) \
-#define XR_LIST_BITS_XrRenderModelFlagsFB(_)
+#define XR_LIST_BITS_XrRenderModelFlagsFB(_) \
+ _(XR_RENDER_MODEL_SUPPORTS_GLTF_2_0_SUBSET_1_BIT_FB, 0x00000001) \
+ _(XR_RENDER_MODEL_SUPPORTS_GLTF_2_0_SUBSET_2_BIT_FB, 0x00000002) \
-#define XR_LIST_BITS_XrCompositionLayerSpaceWarpInfoFlagsFB(_)
+#define XR_LIST_BITS_XrCompositionLayerSpaceWarpInfoFlagsFB(_) \
+ _(XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0x00000001) \
#define XR_LIST_BITS_XrDigitalLensControlFlagsALMALENCE(_) \
_(XR_DIGITAL_LENS_CONTROL_PROCESSING_DISABLE_BIT_ALMALENCE, 0x00000001) \
+#define XR_LIST_BITS_XrCompositionLayerSettingsFlagsFB(_) \
+ _(XR_COMPOSITION_LAYER_SETTINGS_NORMAL_SUPER_SAMPLING_BIT_FB, 0x00000001) \
+ _(XR_COMPOSITION_LAYER_SETTINGS_QUALITY_SUPER_SAMPLING_BIT_FB, 0x00000002) \
+ _(XR_COMPOSITION_LAYER_SETTINGS_NORMAL_SHARPENING_BIT_FB, 0x00000004) \
+ _(XR_COMPOSITION_LAYER_SETTINGS_QUALITY_SHARPENING_BIT_FB, 0x00000008) \
+
+#define XR_LIST_BITS_XrPerformanceMetricsCounterFlagsMETA(_) \
+ _(XR_PERFORMANCE_METRICS_COUNTER_ANY_VALUE_VALID_BIT_META, 0x00000001) \
+ _(XR_PERFORMANCE_METRICS_COUNTER_UINT_VALUE_VALID_BIT_META, 0x00000002) \
+ _(XR_PERFORMANCE_METRICS_COUNTER_FLOAT_VALUE_VALID_BIT_META, 0x00000004) \
+
#define XR_LIST_STRUCT_XrApiLayerProperties(_) \
_(type) \
_(next) \
@@ -1568,6 +1675,23 @@ XR_ENUM_STR(XrResult);
_(nodeId) \
_(pose) \
+#define XR_LIST_STRUCT_XrSpatialGraphStaticNodeBindingCreateInfoMSFT(_) \
+ _(type) \
+ _(next) \
+ _(space) \
+ _(poseInSpace) \
+ _(time) \
+
+#define XR_LIST_STRUCT_XrSpatialGraphNodeBindingPropertiesGetInfoMSFT(_) \
+ _(type) \
+ _(next) \
+
+#define XR_LIST_STRUCT_XrSpatialGraphNodeBindingPropertiesMSFT(_) \
+ _(type) \
+ _(next) \
+ _(nodeId) \
+ _(poseInNodeSpace) \
+
#define XR_LIST_STRUCT_XrSystemHandTrackingPropertiesEXT(_) \
_(type) \
_(next) \
@@ -1762,6 +1886,19 @@ XR_ENUM_STR(XrResult);
_(next) \
_(flags) \
+#define XR_LIST_STRUCT_XrInteractionProfileDpadBindingEXT(_) \
+ _(type) \
+ _(next) \
+ _(binding) \
+ _(actionSet) \
+ _(forceThreshold) \
+ _(forceThresholdReleased) \
+ _(centerRegion) \
+ _(wedgeAngle) \
+ _(isSticky) \
+ _(onHaptic) \
+ _(offHaptic) \
+
#define XR_LIST_STRUCT_XrInteractionProfileAnalogThresholdVALVE(_) \
_(type) \
_(next) \
@@ -2047,6 +2184,52 @@ XR_ENUM_STR(XrResult);
_(next) \
_(capsules) \
+#define XR_LIST_STRUCT_XrSystemSpatialEntityPropertiesFB(_) \
+ _(type) \
+ _(next) \
+ _(supportsSpatialEntity) \
+
+#define XR_LIST_STRUCT_XrSpatialAnchorCreateInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(space) \
+ _(poseInSpace) \
+ _(time) \
+
+#define XR_LIST_STRUCT_XrSpaceComponentStatusSetInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(componentType) \
+ _(enabled) \
+ _(timeout) \
+
+#define XR_LIST_STRUCT_XrSpaceComponentStatusFB(_) \
+ _(type) \
+ _(next) \
+ _(enabled) \
+ _(changePending) \
+
+#define XR_LIST_STRUCT_XrUuidEXT(_) \
+ _(data) \
+
+#define XR_LIST_STRUCT_XrEventDataSpatialAnchorCreateCompleteFB(_) \
+ _(type) \
+ _(next) \
+ _(requestId) \
+ _(result) \
+ _(space) \
+ _(uuid) \
+
+#define XR_LIST_STRUCT_XrEventDataSpaceSetStatusCompleteFB(_) \
+ _(type) \
+ _(next) \
+ _(requestId) \
+ _(result) \
+ _(space) \
+ _(uuid) \
+ _(componentType) \
+ _(enabled) \
+
#define XR_LIST_STRUCT_XrFoveationProfileCreateInfoFB(_) \
_(type) \
_(next) \
@@ -2157,6 +2340,13 @@ XR_ENUM_STR(XrResult);
_(next) \
_(textureColorMap) \
+#define XR_LIST_STRUCT_XrPassthroughBrightnessContrastSaturationFB(_) \
+ _(type) \
+ _(next) \
+ _(brightness) \
+ _(contrast) \
+ _(saturation) \
+
#define XR_LIST_STRUCT_XrEventDataPassthroughStateChangedFB(_) \
_(type) \
_(next) \
@@ -2193,6 +2383,11 @@ XR_ENUM_STR(XrResult);
_(next) \
_(supportsRenderModelLoading) \
+#define XR_LIST_STRUCT_XrRenderModelCapabilitiesRequestFB(_) \
+ _(type) \
+ _(next) \
+ _(flags) \
+
#define XR_LIST_STRUCT_XrViewLocateFoveatedRenderingVARJO(_) \
_(type) \
_(next) \
@@ -2248,6 +2443,92 @@ XR_ENUM_STR(XrResult);
_(spatialAnchorStore) \
_(spatialAnchorPersistenceName) \
+#define XR_LIST_STRUCT_XrSpaceQueryInfoBaseHeaderFB(_) \
+ _(type) \
+ _(next) \
+
+#define XR_LIST_STRUCT_XrSpaceFilterInfoBaseHeaderFB(_) \
+ _(type) \
+ _(next) \
+
+#define XR_LIST_STRUCT_XrSpaceQueryInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(queryAction) \
+ _(maxResultCount) \
+ _(timeout) \
+ _(filter) \
+ _(excludeFilter) \
+
+#define XR_LIST_STRUCT_XrSpaceStorageLocationFilterInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(location) \
+
+#define XR_LIST_STRUCT_XrSpaceUuidFilterInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(uuidCount) \
+ _(uuids) \
+
+#define XR_LIST_STRUCT_XrSpaceComponentFilterInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(componentType) \
+
+#define XR_LIST_STRUCT_XrSpaceQueryResultFB(_) \
+ _(space) \
+ _(uuid) \
+
+#define XR_LIST_STRUCT_XrSpaceQueryResultsFB(_) \
+ _(type) \
+ _(next) \
+ _(resultCapacityInput) \
+ _(resultCountOutput) \
+ _(results) \
+
+#define XR_LIST_STRUCT_XrEventDataSpaceQueryResultsAvailableFB(_) \
+ _(type) \
+ _(next) \
+ _(requestId) \
+
+#define XR_LIST_STRUCT_XrEventDataSpaceQueryCompleteFB(_) \
+ _(type) \
+ _(next) \
+ _(requestId) \
+ _(result) \
+
+#define XR_LIST_STRUCT_XrSpaceSaveInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(space) \
+ _(location) \
+ _(persistenceMode) \
+
+#define XR_LIST_STRUCT_XrSpaceEraseInfoFB(_) \
+ _(type) \
+ _(next) \
+ _(space) \
+ _(location) \
+
+#define XR_LIST_STRUCT_XrEventDataSpaceSaveCompleteFB(_) \
+ _(type) \
+ _(next) \
+ _(requestId) \
+ _(result) \
+ _(space) \
+ _(uuid) \
+ _(location) \
+
+#define XR_LIST_STRUCT_XrEventDataSpaceEraseCompleteFB(_) \
+ _(type) \
+ _(next) \
+ _(requestId) \
+ _(result) \
+ _(space) \
+ _(uuid) \
+ _(location) \
+
#define XR_LIST_STRUCT_XrSwapchainImageFoveationVulkanFB(_) \
_(type) \
_(next) \
@@ -2313,14 +2594,42 @@ XR_ENUM_STR(XrResult);
_(next) \
_(flags) \
+#define XR_LIST_STRUCT_XrSpaceContainerFB(_) \
+ _(type) \
+ _(next) \
+ _(uuidCapacityInput) \
+ _(uuidCountOutput) \
+ _(uuids) \
+
#define XR_LIST_STRUCT_XrPassthroughKeyboardHandsIntensityFB(_) \
_(type) \
_(next) \
_(leftHandIntensity) \
_(rightHandIntensity) \
-#define XR_LIST_STRUCT_XrUuidEXT(_) \
- _(data) \
+#define XR_LIST_STRUCT_XrCompositionLayerSettingsFB(_) \
+ _(type) \
+ _(next) \
+ _(layerFlags) \
+
+#define XR_LIST_STRUCT_XrVulkanSwapchainCreateInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(additionalCreateFlags) \
+ _(additionalUsageFlags) \
+
+#define XR_LIST_STRUCT_XrPerformanceMetricsStateMETA(_) \
+ _(type) \
+ _(next) \
+ _(enabled) \
+
+#define XR_LIST_STRUCT_XrPerformanceMetricsCounterMETA(_) \
+ _(type) \
+ _(next) \
+ _(counterFlags) \
+ _(counterUnit) \
+ _(uintValue) \
+ _(floatValue) \
@@ -2398,6 +2707,9 @@ XR_ENUM_STR(XrResult);
_(XrCompositionLayerAlphaBlendFB, XR_TYPE_COMPOSITION_LAYER_ALPHA_BLEND_FB) \
_(XrViewConfigurationDepthRangeEXT, XR_TYPE_VIEW_CONFIGURATION_DEPTH_RANGE_EXT) \
_(XrSpatialGraphNodeSpaceCreateInfoMSFT, XR_TYPE_SPATIAL_GRAPH_NODE_SPACE_CREATE_INFO_MSFT) \
+ _(XrSpatialGraphStaticNodeBindingCreateInfoMSFT, XR_TYPE_SPATIAL_GRAPH_STATIC_NODE_BINDING_CREATE_INFO_MSFT) \
+ _(XrSpatialGraphNodeBindingPropertiesGetInfoMSFT, XR_TYPE_SPATIAL_GRAPH_NODE_BINDING_PROPERTIES_GET_INFO_MSFT) \
+ _(XrSpatialGraphNodeBindingPropertiesMSFT, XR_TYPE_SPATIAL_GRAPH_NODE_BINDING_PROPERTIES_MSFT) \
_(XrSystemHandTrackingPropertiesEXT, XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT) \
_(XrHandTrackerCreateInfoEXT, XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT) \
_(XrHandJointsLocateInfoEXT, XR_TYPE_HAND_JOINTS_LOCATE_INFO_EXT) \
@@ -2423,6 +2735,7 @@ XR_ENUM_STR(XrResult);
_(XrCompositionLayerReprojectionInfoMSFT, XR_TYPE_COMPOSITION_LAYER_REPROJECTION_INFO_MSFT) \
_(XrCompositionLayerReprojectionPlaneOverrideMSFT, XR_TYPE_COMPOSITION_LAYER_REPROJECTION_PLANE_OVERRIDE_MSFT) \
_(XrCompositionLayerSecureContentFB, XR_TYPE_COMPOSITION_LAYER_SECURE_CONTENT_FB) \
+ _(XrInteractionProfileDpadBindingEXT, XR_TYPE_INTERACTION_PROFILE_DPAD_BINDING_EXT) \
_(XrInteractionProfileAnalogThresholdVALVE, XR_TYPE_INTERACTION_PROFILE_ANALOG_THRESHOLD_VALVE) \
_(XrHandJointsMotionRangeInfoEXT, XR_TYPE_HAND_JOINTS_MOTION_RANGE_INFO_EXT) \
_(XrSceneObserverCreateInfoMSFT, XR_TYPE_SCENE_OBSERVER_CREATE_INFO_MSFT) \
@@ -2457,6 +2770,12 @@ XR_ENUM_STR(XrResult);
_(XrHandTrackingScaleFB, XR_TYPE_HAND_TRACKING_SCALE_FB) \
_(XrHandTrackingAimStateFB, XR_TYPE_HAND_TRACKING_AIM_STATE_FB) \
_(XrHandTrackingCapsulesStateFB, XR_TYPE_HAND_TRACKING_CAPSULES_STATE_FB) \
+ _(XrSystemSpatialEntityPropertiesFB, XR_TYPE_SYSTEM_SPATIAL_ENTITY_PROPERTIES_FB) \
+ _(XrSpatialAnchorCreateInfoFB, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_FB) \
+ _(XrSpaceComponentStatusSetInfoFB, XR_TYPE_SPACE_COMPONENT_STATUS_SET_INFO_FB) \
+ _(XrSpaceComponentStatusFB, XR_TYPE_SPACE_COMPONENT_STATUS_FB) \
+ _(XrEventDataSpatialAnchorCreateCompleteFB, XR_TYPE_EVENT_DATA_SPATIAL_ANCHOR_CREATE_COMPLETE_FB) \
+ _(XrEventDataSpaceSetStatusCompleteFB, XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB) \
_(XrFoveationProfileCreateInfoFB, XR_TYPE_FOVEATION_PROFILE_CREATE_INFO_FB) \
_(XrSwapchainCreateInfoFoveationFB, XR_TYPE_SWAPCHAIN_CREATE_INFO_FOVEATION_FB) \
_(XrSwapchainStateFoveationFB, XR_TYPE_SWAPCHAIN_STATE_FOVEATION_FB) \
@@ -2474,12 +2793,14 @@ XR_ENUM_STR(XrResult);
_(XrPassthroughStyleFB, XR_TYPE_PASSTHROUGH_STYLE_FB) \
_(XrPassthroughColorMapMonoToRgbaFB, XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_RGBA_FB) \
_(XrPassthroughColorMapMonoToMonoFB, XR_TYPE_PASSTHROUGH_COLOR_MAP_MONO_TO_MONO_FB) \
+ _(XrPassthroughBrightnessContrastSaturationFB, XR_TYPE_PASSTHROUGH_BRIGHTNESS_CONTRAST_SATURATION_FB) \
_(XrEventDataPassthroughStateChangedFB, XR_TYPE_EVENT_DATA_PASSTHROUGH_STATE_CHANGED_FB) \
_(XrRenderModelPathInfoFB, XR_TYPE_RENDER_MODEL_PATH_INFO_FB) \
_(XrRenderModelPropertiesFB, XR_TYPE_RENDER_MODEL_PROPERTIES_FB) \
_(XrRenderModelBufferFB, XR_TYPE_RENDER_MODEL_BUFFER_FB) \
_(XrRenderModelLoadInfoFB, XR_TYPE_RENDER_MODEL_LOAD_INFO_FB) \
_(XrSystemRenderModelPropertiesFB, XR_TYPE_SYSTEM_RENDER_MODEL_PROPERTIES_FB) \
+ _(XrRenderModelCapabilitiesRequestFB, XR_TYPE_RENDER_MODEL_CAPABILITIES_REQUEST_FB) \
_(XrViewLocateFoveatedRenderingVARJO, XR_TYPE_VIEW_LOCATE_FOVEATED_RENDERING_VARJO) \
_(XrFoveatedViewConfigurationViewVARJO, XR_TYPE_FOVEATED_VIEW_CONFIGURATION_VIEW_VARJO) \
_(XrSystemFoveatedRenderingPropertiesVARJO, XR_TYPE_SYSTEM_FOVEATED_RENDERING_PROPERTIES_VARJO) \
@@ -2489,10 +2810,25 @@ XR_ENUM_STR(XrResult);
_(XrMarkerSpaceCreateInfoVARJO, XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO) \
_(XrSpatialAnchorPersistenceInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT) \
_(XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT) \
+ _(XrSpaceQueryInfoFB, XR_TYPE_SPACE_QUERY_INFO_FB) \
+ _(XrSpaceStorageLocationFilterInfoFB, XR_TYPE_SPACE_STORAGE_LOCATION_FILTER_INFO_FB) \
+ _(XrSpaceUuidFilterInfoFB, XR_TYPE_SPACE_UUID_FILTER_INFO_FB) \
+ _(XrSpaceComponentFilterInfoFB, XR_TYPE_SPACE_COMPONENT_FILTER_INFO_FB) \
+ _(XrSpaceQueryResultsFB, XR_TYPE_SPACE_QUERY_RESULTS_FB) \
+ _(XrEventDataSpaceQueryResultsAvailableFB, XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB) \
+ _(XrEventDataSpaceQueryCompleteFB, XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB) \
+ _(XrSpaceSaveInfoFB, XR_TYPE_SPACE_SAVE_INFO_FB) \
+ _(XrSpaceEraseInfoFB, XR_TYPE_SPACE_ERASE_INFO_FB) \
+ _(XrEventDataSpaceSaveCompleteFB, XR_TYPE_EVENT_DATA_SPACE_SAVE_COMPLETE_FB) \
+ _(XrEventDataSpaceEraseCompleteFB, XR_TYPE_EVENT_DATA_SPACE_ERASE_COMPLETE_FB) \
_(XrCompositionLayerSpaceWarpInfoFB, XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB) \
_(XrSystemSpaceWarpPropertiesFB, XR_TYPE_SYSTEM_SPACE_WARP_PROPERTIES_FB) \
_(XrDigitalLensControlALMALENCE, XR_TYPE_DIGITAL_LENS_CONTROL_ALMALENCE) \
+ _(XrSpaceContainerFB, XR_TYPE_SPACE_CONTAINER_FB) \
_(XrPassthroughKeyboardHandsIntensityFB, XR_TYPE_PASSTHROUGH_KEYBOARD_HANDS_INTENSITY_FB) \
+ _(XrCompositionLayerSettingsFB, XR_TYPE_COMPOSITION_LAYER_SETTINGS_FB) \
+ _(XrPerformanceMetricsStateMETA, XR_TYPE_PERFORMANCE_METRICS_STATE_META) \
+ _(XrPerformanceMetricsCounterMETA, XR_TYPE_PERFORMANCE_METRICS_COUNTER_META) \
@@ -2596,6 +2932,7 @@ XR_ENUM_STR(XrResult);
_(XrVulkanGraphicsDeviceGetInfoKHR, XR_TYPE_VULKAN_GRAPHICS_DEVICE_GET_INFO_KHR) \
_(XrSwapchainImageFoveationVulkanFB, XR_TYPE_SWAPCHAIN_IMAGE_FOVEATION_VULKAN_FB) \
_(XrSwapchainStateSamplerVulkanFB, XR_TYPE_SWAPCHAIN_STATE_SAMPLER_VULKAN_FB) \
+ _(XrVulkanSwapchainCreateInfoMETA, XR_TYPE_VULKAN_SWAPCHAIN_CREATE_INFO_META) \
#else
@@ -2698,6 +3035,7 @@ XR_ENUM_STR(XrResult);
_(XR_FB_android_surface_swapchain_create, 71) \
_(XR_FB_swapchain_update_state, 72) \
_(XR_FB_composition_layer_secure_content, 73) \
+ _(XR_EXT_dpad_binding, 79) \
_(XR_VALVE_analog_threshold, 80) \
_(XR_EXT_hand_joints_motion_range, 81) \
_(XR_KHR_loader_init, 89) \
@@ -2714,10 +3052,13 @@ XR_ENUM_STR(XrResult);
_(XR_HTCX_vive_tracker_interaction, 104) \
_(XR_HTC_facial_tracking, 105) \
_(XR_HTC_vive_focus3_controller_interaction, 106) \
+ _(XR_HTC_hand_interaction, 107) \
+ _(XR_HTC_vive_wrist_tracker_interaction, 108) \
_(XR_FB_color_space, 109) \
_(XR_FB_hand_tracking_mesh, 111) \
_(XR_FB_hand_tracking_aim, 112) \
_(XR_FB_hand_tracking_capsules, 113) \
+ _(XR_FB_spatial_entity, 114) \
_(XR_FB_foveation, 115) \
_(XR_FB_foveation_configuration, 116) \
_(XR_FB_keyboard_tracking, 117) \
@@ -2729,7 +3070,11 @@ XR_ENUM_STR(XrResult);
_(XR_VARJO_composition_layer_depth_test, 123) \
_(XR_VARJO_environment_depth_estimation, 124) \
_(XR_VARJO_marker_tracking, 125) \
+ _(XR_VARJO_view_offset, 126) \
_(XR_MSFT_spatial_anchor_persistence, 143) \
+ _(XR_ULTRALEAP_hand_tracking_forearm, 150) \
+ _(XR_FB_spatial_entity_query, 157) \
+ _(XR_FB_spatial_entity_storage, 159) \
_(XR_OCULUS_audio_device_guid, 160) \
_(XR_FB_foveation_vulkan, 161) \
_(XR_FB_swapchain_update_state_android_surface, 162) \
@@ -2738,7 +3083,11 @@ XR_ENUM_STR(XrResult);
_(XR_KHR_swapchain_usage_input_attachment_bit, 166) \
_(XR_FB_space_warp, 172) \
_(XR_ALMALENCE_digital_lens_control, 197) \
+ _(XR_FB_spatial_entity_container, 200) \
_(XR_FB_passthrough_keyboard_hands, 204) \
+ _(XR_FB_composition_layer_settings, 205) \
+ _(XR_META_vulkan_swapchain_create_info, 228) \
+ _(XR_META_performance_metrics, 233) \
_(XR_EXT_uuid, 300) \
diff --git a/thirdparty/openxr/src/common/xr_linear.h b/thirdparty/openxr/src/common/xr_linear.h
index 9ffb49a4b6..1f0e803b7a 100644
--- a/thirdparty/openxr/src/common/xr_linear.h
+++ b/thirdparty/openxr/src/common/xr_linear.h
@@ -126,12 +126,12 @@ static const XrColor4f XrColorCyan = {0.0f, 1.0f, 1.0f, 1.0f};
static const XrColor4f XrColorLightGrey = {0.7f, 0.7f, 0.7f, 1.0f};
static const XrColor4f XrColorDarkGrey = {0.3f, 0.3f, 0.3f, 1.0f};
-enum GraphicsAPI { GRAPHICS_VULKAN, GRAPHICS_OPENGL, GRAPHICS_OPENGL_ES, GRAPHICS_D3D };
+typedef enum GraphicsAPI { GRAPHICS_VULKAN, GRAPHICS_OPENGL, GRAPHICS_OPENGL_ES, GRAPHICS_D3D } GraphicsAPI;
// Column-major, pre-multiplied. This type does not exist in the OpenXR API and is provided for convenience.
-struct XrMatrix4x4f {
+typedef struct XrMatrix4x4f {
float m[16];
-};
+} XrMatrix4x4f;
inline static float XrRcpSqrt(const float x) {
const float SMALLEST_NON_DENORMAL = 1.1754943508222875e-038f; // ( 1U << 23 )
diff --git a/thirdparty/openxr/src/xr_generated_dispatch_table.c b/thirdparty/openxr/src/xr_generated_dispatch_table.c
index 79fbefc52a..91fa0c3ca0 100644
--- a/thirdparty/openxr/src/xr_generated_dispatch_table.c
+++ b/thirdparty/openxr/src/xr_generated_dispatch_table.c
@@ -202,6 +202,9 @@ void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table,
// ---- XR_MSFT_spatial_graph_bridge extension commands
(get_inst_proc_addr(instance, "xrCreateSpatialGraphNodeSpaceMSFT", (PFN_xrVoidFunction*)&table->CreateSpatialGraphNodeSpaceMSFT));
+ (get_inst_proc_addr(instance, "xrTryCreateSpatialGraphStaticNodeBindingMSFT", (PFN_xrVoidFunction*)&table->TryCreateSpatialGraphStaticNodeBindingMSFT));
+ (get_inst_proc_addr(instance, "xrDestroySpatialGraphNodeBindingMSFT", (PFN_xrVoidFunction*)&table->DestroySpatialGraphNodeBindingMSFT));
+ (get_inst_proc_addr(instance, "xrGetSpatialGraphNodeBindingPropertiesMSFT", (PFN_xrVoidFunction*)&table->GetSpatialGraphNodeBindingPropertiesMSFT));
// ---- XR_EXT_hand_tracking extension commands
(get_inst_proc_addr(instance, "xrCreateHandTrackerEXT", (PFN_xrVoidFunction*)&table->CreateHandTrackerEXT));
@@ -269,6 +272,13 @@ void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table,
// ---- XR_FB_hand_tracking_mesh extension commands
(get_inst_proc_addr(instance, "xrGetHandMeshFB", (PFN_xrVoidFunction*)&table->GetHandMeshFB));
+ // ---- XR_FB_spatial_entity extension commands
+ (get_inst_proc_addr(instance, "xrCreateSpatialAnchorFB", (PFN_xrVoidFunction*)&table->CreateSpatialAnchorFB));
+ (get_inst_proc_addr(instance, "xrGetSpaceUuidFB", (PFN_xrVoidFunction*)&table->GetSpaceUuidFB));
+ (get_inst_proc_addr(instance, "xrEnumerateSpaceSupportedComponentsFB", (PFN_xrVoidFunction*)&table->EnumerateSpaceSupportedComponentsFB));
+ (get_inst_proc_addr(instance, "xrSetSpaceComponentStatusFB", (PFN_xrVoidFunction*)&table->SetSpaceComponentStatusFB));
+ (get_inst_proc_addr(instance, "xrGetSpaceComponentStatusFB", (PFN_xrVoidFunction*)&table->GetSpaceComponentStatusFB));
+
// ---- XR_FB_foveation extension commands
(get_inst_proc_addr(instance, "xrCreateFoveationProfileFB", (PFN_xrVoidFunction*)&table->CreateFoveationProfileFB));
(get_inst_proc_addr(instance, "xrDestroyFoveationProfileFB", (PFN_xrVoidFunction*)&table->DestroyFoveationProfileFB));
@@ -316,6 +326,9 @@ void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table,
(get_inst_proc_addr(instance, "xrGetMarkerSizeVARJO", (PFN_xrVoidFunction*)&table->GetMarkerSizeVARJO));
(get_inst_proc_addr(instance, "xrCreateMarkerSpaceVARJO", (PFN_xrVoidFunction*)&table->CreateMarkerSpaceVARJO));
+ // ---- XR_VARJO_view_offset extension commands
+ (get_inst_proc_addr(instance, "xrSetViewOffsetVARJO", (PFN_xrVoidFunction*)&table->SetViewOffsetVARJO));
+
// ---- XR_MSFT_spatial_anchor_persistence extension commands
(get_inst_proc_addr(instance, "xrCreateSpatialAnchorStoreConnectionMSFT", (PFN_xrVoidFunction*)&table->CreateSpatialAnchorStoreConnectionMSFT));
(get_inst_proc_addr(instance, "xrDestroySpatialAnchorStoreConnectionMSFT", (PFN_xrVoidFunction*)&table->DestroySpatialAnchorStoreConnectionMSFT));
@@ -325,6 +338,14 @@ void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table,
(get_inst_proc_addr(instance, "xrUnpersistSpatialAnchorMSFT", (PFN_xrVoidFunction*)&table->UnpersistSpatialAnchorMSFT));
(get_inst_proc_addr(instance, "xrClearSpatialAnchorStoreMSFT", (PFN_xrVoidFunction*)&table->ClearSpatialAnchorStoreMSFT));
+ // ---- XR_FB_spatial_entity_query extension commands
+ (get_inst_proc_addr(instance, "xrQuerySpacesFB", (PFN_xrVoidFunction*)&table->QuerySpacesFB));
+ (get_inst_proc_addr(instance, "xrRetrieveSpaceQueryResultsFB", (PFN_xrVoidFunction*)&table->RetrieveSpaceQueryResultsFB));
+
+ // ---- XR_FB_spatial_entity_storage extension commands
+ (get_inst_proc_addr(instance, "xrSaveSpaceFB", (PFN_xrVoidFunction*)&table->SaveSpaceFB));
+ (get_inst_proc_addr(instance, "xrEraseSpaceFB", (PFN_xrVoidFunction*)&table->EraseSpaceFB));
+
// ---- XR_OCULUS_audio_device_guid extension commands
#if defined(XR_USE_PLATFORM_WIN32)
(get_inst_proc_addr(instance, "xrGetAudioOutputDeviceGuidOculus", (PFN_xrVoidFunction*)&table->GetAudioOutputDeviceGuidOculus));
@@ -336,8 +357,17 @@ void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table,
// ---- XR_ALMALENCE_digital_lens_control extension commands
(get_inst_proc_addr(instance, "xrSetDigitalLensControlALMALENCE", (PFN_xrVoidFunction*)&table->SetDigitalLensControlALMALENCE));
+ // ---- XR_FB_spatial_entity_container extension commands
+ (get_inst_proc_addr(instance, "xrGetSpaceContainerFB", (PFN_xrVoidFunction*)&table->GetSpaceContainerFB));
+
// ---- XR_FB_passthrough_keyboard_hands extension commands
(get_inst_proc_addr(instance, "xrPassthroughLayerSetKeyboardHandsIntensityFB", (PFN_xrVoidFunction*)&table->PassthroughLayerSetKeyboardHandsIntensityFB));
+
+ // ---- XR_META_performance_metrics extension commands
+ (get_inst_proc_addr(instance, "xrEnumeratePerformanceMetricsCounterPathsMETA", (PFN_xrVoidFunction*)&table->EnumeratePerformanceMetricsCounterPathsMETA));
+ (get_inst_proc_addr(instance, "xrSetPerformanceMetricsStateMETA", (PFN_xrVoidFunction*)&table->SetPerformanceMetricsStateMETA));
+ (get_inst_proc_addr(instance, "xrGetPerformanceMetricsStateMETA", (PFN_xrVoidFunction*)&table->GetPerformanceMetricsStateMETA));
+ (get_inst_proc_addr(instance, "xrQueryPerformanceMetricsCounterMETA", (PFN_xrVoidFunction*)&table->QueryPerformanceMetricsCounterMETA));
}
diff --git a/thirdparty/openxr/src/xr_generated_dispatch_table.h b/thirdparty/openxr/src/xr_generated_dispatch_table.h
index 34e0de93f5..51d48bef43 100644
--- a/thirdparty/openxr/src/xr_generated_dispatch_table.h
+++ b/thirdparty/openxr/src/xr_generated_dispatch_table.h
@@ -205,6 +205,9 @@ struct XrGeneratedDispatchTable {
// ---- XR_MSFT_spatial_graph_bridge extension commands
PFN_xrCreateSpatialGraphNodeSpaceMSFT CreateSpatialGraphNodeSpaceMSFT;
+ PFN_xrTryCreateSpatialGraphStaticNodeBindingMSFT TryCreateSpatialGraphStaticNodeBindingMSFT;
+ PFN_xrDestroySpatialGraphNodeBindingMSFT DestroySpatialGraphNodeBindingMSFT;
+ PFN_xrGetSpatialGraphNodeBindingPropertiesMSFT GetSpatialGraphNodeBindingPropertiesMSFT;
// ---- XR_EXT_hand_tracking extension commands
PFN_xrCreateHandTrackerEXT CreateHandTrackerEXT;
@@ -272,6 +275,13 @@ struct XrGeneratedDispatchTable {
// ---- XR_FB_hand_tracking_mesh extension commands
PFN_xrGetHandMeshFB GetHandMeshFB;
+ // ---- XR_FB_spatial_entity extension commands
+ PFN_xrCreateSpatialAnchorFB CreateSpatialAnchorFB;
+ PFN_xrGetSpaceUuidFB GetSpaceUuidFB;
+ PFN_xrEnumerateSpaceSupportedComponentsFB EnumerateSpaceSupportedComponentsFB;
+ PFN_xrSetSpaceComponentStatusFB SetSpaceComponentStatusFB;
+ PFN_xrGetSpaceComponentStatusFB GetSpaceComponentStatusFB;
+
// ---- XR_FB_foveation extension commands
PFN_xrCreateFoveationProfileFB CreateFoveationProfileFB;
PFN_xrDestroyFoveationProfileFB DestroyFoveationProfileFB;
@@ -319,6 +329,9 @@ struct XrGeneratedDispatchTable {
PFN_xrGetMarkerSizeVARJO GetMarkerSizeVARJO;
PFN_xrCreateMarkerSpaceVARJO CreateMarkerSpaceVARJO;
+ // ---- XR_VARJO_view_offset extension commands
+ PFN_xrSetViewOffsetVARJO SetViewOffsetVARJO;
+
// ---- XR_MSFT_spatial_anchor_persistence extension commands
PFN_xrCreateSpatialAnchorStoreConnectionMSFT CreateSpatialAnchorStoreConnectionMSFT;
PFN_xrDestroySpatialAnchorStoreConnectionMSFT DestroySpatialAnchorStoreConnectionMSFT;
@@ -328,6 +341,14 @@ struct XrGeneratedDispatchTable {
PFN_xrUnpersistSpatialAnchorMSFT UnpersistSpatialAnchorMSFT;
PFN_xrClearSpatialAnchorStoreMSFT ClearSpatialAnchorStoreMSFT;
+ // ---- XR_FB_spatial_entity_query extension commands
+ PFN_xrQuerySpacesFB QuerySpacesFB;
+ PFN_xrRetrieveSpaceQueryResultsFB RetrieveSpaceQueryResultsFB;
+
+ // ---- XR_FB_spatial_entity_storage extension commands
+ PFN_xrSaveSpaceFB SaveSpaceFB;
+ PFN_xrEraseSpaceFB EraseSpaceFB;
+
// ---- XR_OCULUS_audio_device_guid extension commands
#if defined(XR_USE_PLATFORM_WIN32)
PFN_xrGetAudioOutputDeviceGuidOculus GetAudioOutputDeviceGuidOculus;
@@ -339,8 +360,17 @@ struct XrGeneratedDispatchTable {
// ---- XR_ALMALENCE_digital_lens_control extension commands
PFN_xrSetDigitalLensControlALMALENCE SetDigitalLensControlALMALENCE;
+ // ---- XR_FB_spatial_entity_container extension commands
+ PFN_xrGetSpaceContainerFB GetSpaceContainerFB;
+
// ---- XR_FB_passthrough_keyboard_hands extension commands
PFN_xrPassthroughLayerSetKeyboardHandsIntensityFB PassthroughLayerSetKeyboardHandsIntensityFB;
+
+ // ---- XR_META_performance_metrics extension commands
+ PFN_xrEnumeratePerformanceMetricsCounterPathsMETA EnumeratePerformanceMetricsCounterPathsMETA;
+ PFN_xrSetPerformanceMetricsStateMETA SetPerformanceMetricsStateMETA;
+ PFN_xrGetPerformanceMetricsStateMETA GetPerformanceMetricsStateMETA;
+ PFN_xrQueryPerformanceMetricsCounterMETA QueryPerformanceMetricsCounterMETA;
};