summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/bind/core_bind.cpp2
-rw-r--r--core/bind/core_bind.h3
-rw-r--r--core/class_db.cpp13
-rw-r--r--core/class_db.h1
-rw-r--r--core/color.cpp8
-rw-r--r--core/color.h7
-rw-r--r--core/command_queue_mt.cpp4
-rw-r--r--core/command_queue_mt.h38
-rw-r--r--core/engine.cpp4
-rw-r--r--core/engine.h3
-rw-r--r--core/image.cpp496
-rw-r--r--core/image.h82
-rw-r--r--core/io/config_file.cpp3
-rw-r--r--core/io/file_access_network.cpp6
-rw-r--r--core/io/file_access_network.h6
-rw-r--r--core/io/ip.cpp4
-rw-r--r--core/io/resource_format_binary.cpp2
-rw-r--r--core/io/resource_loader.cpp4
-rw-r--r--core/make_binders.py2
-rw-r--r--core/math/basis.cpp12
-rw-r--r--core/math/basis.h3
-rw-r--r--core/math/camera_matrix.cpp58
-rw-r--r--core/math/camera_matrix.h19
-rw-r--r--core/math/vector3.cpp4
-rw-r--r--core/math/vector3.h10
-rw-r--r--core/math/vector3i.cpp55
-rw-r--r--core/math/vector3i.h272
-rw-r--r--core/oa_hash_map.h16
-rw-r--r--core/os/os.cpp4
-rw-r--r--core/os/os.h2
-rw-r--r--core/os/semaphore.cpp6
-rw-r--r--core/os/semaphore.h40
-rw-r--r--core/os/thread_dummy.cpp4
-rw-r--r--core/os/thread_dummy.h4
-rw-r--r--core/rid.h152
-rw-r--r--core/rid_owner.cpp (renamed from core/rid.cpp)14
-rw-r--r--core/rid_owner.h406
-rw-r--r--core/spin_lock.h50
-rw-r--r--core/thread_work_pool.cpp83
-rw-r--r--core/thread_work_pool.h108
-rw-r--r--core/ustring.cpp11
-rw-r--r--core/ustring.h1
42 files changed, 1718 insertions, 304 deletions
diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp
index e61e392a79..1dc48a95a0 100644
--- a/core/bind/core_bind.cpp
+++ b/core/bind/core_bind.cpp
@@ -2612,7 +2612,7 @@ void _Semaphore::_bind_methods() {
_Semaphore::_Semaphore() {
- semaphore = Semaphore::create();
+ semaphore = SemaphoreOld::create();
}
_Semaphore::~_Semaphore() {
diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h
index 87da51f97e..015bcd965e 100644
--- a/core/bind/core_bind.h
+++ b/core/bind/core_bind.h
@@ -106,6 +106,7 @@ public:
enum VideoDriver {
VIDEO_DRIVER_GLES3,
VIDEO_DRIVER_GLES2,
+ VIDEO_DRIVER_VULKAN,
};
enum PowerState {
@@ -642,7 +643,7 @@ public:
class _Semaphore : public Reference {
GDCLASS(_Semaphore, Reference);
- Semaphore *semaphore;
+ SemaphoreOld *semaphore;
static void _bind_methods();
diff --git a/core/class_db.cpp b/core/class_db.cpp
index 8800f51778..a2941d70f6 100644
--- a/core/class_db.cpp
+++ b/core/class_db.cpp
@@ -335,6 +335,19 @@ StringName ClassDB::get_parent_class_nocheck(const StringName &p_class) {
return ti->inherits;
}
+StringName ClassDB::get_compatibility_remapped_class(const StringName &p_class) {
+
+ if (classes.has(p_class)) {
+ return p_class;
+ }
+
+ if (compat_classes.has(p_class)) {
+ return compat_classes[p_class];
+ }
+
+ return p_class;
+}
+
StringName ClassDB::get_parent_class(const StringName &p_class) {
OBJTYPE_RLOCK;
diff --git a/core/class_db.h b/core/class_db.h
index 34301d6cba..404b04f2d0 100644
--- a/core/class_db.h
+++ b/core/class_db.h
@@ -218,6 +218,7 @@ public:
static void get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes);
static StringName get_parent_class_nocheck(const StringName &p_class);
static StringName get_parent_class(const StringName &p_class);
+ static StringName get_compatibility_remapped_class(const StringName &p_class);
static bool class_exists(const StringName &p_class);
static bool is_parent_class(const StringName &p_class, const StringName &p_inherits);
static bool can_instance(const StringName &p_class);
diff --git a/core/color.cpp b/core/color.cpp
index 1baa8af45d..cb3068c487 100644
--- a/core/color.cpp
+++ b/core/color.cpp
@@ -529,14 +529,6 @@ Color Color::operator+(const Color &p_color) const {
a + p_color.a);
}
-void Color::operator+=(const Color &p_color) {
-
- r = r + p_color.r;
- g = g + p_color.g;
- b = b + p_color.b;
- a = a + p_color.a;
-}
-
Color Color::operator-(const Color &p_color) const {
return Color(
diff --git a/core/color.h b/core/color.h
index cfdd211b69..75a2b07d71 100644
--- a/core/color.h
+++ b/core/color.h
@@ -70,7 +70,12 @@ struct Color {
}
Color operator+(const Color &p_color) const;
- void operator+=(const Color &p_color);
+ _FORCE_INLINE_ void operator+=(const Color &p_color) {
+ r = r + p_color.r;
+ g = g + p_color.g;
+ b = b + p_color.b;
+ a = a + p_color.a;
+ }
Color operator-() const;
Color operator-(const Color &p_color) const;
diff --git a/core/command_queue_mt.cpp b/core/command_queue_mt.cpp
index c20735939d..861ca8d1d3 100644
--- a/core/command_queue_mt.cpp
+++ b/core/command_queue_mt.cpp
@@ -111,11 +111,11 @@ CommandQueueMT::CommandQueueMT(bool p_sync) {
for (int i = 0; i < SYNC_SEMAPHORES; i++) {
- sync_sems[i].sem = Semaphore::create();
+ sync_sems[i].sem = SemaphoreOld::create();
sync_sems[i].in_use = false;
}
if (p_sync)
- sync = Semaphore::create();
+ sync = SemaphoreOld::create();
else
sync = NULL;
}
diff --git a/core/command_queue_mt.h b/core/command_queue_mt.h
index e5f93bcc36..2b6e0201f0 100644
--- a/core/command_queue_mt.h
+++ b/core/command_queue_mt.h
@@ -52,9 +52,17 @@
#define _COMMA_11 ,
#define _COMMA_12 ,
#define _COMMA_13 ,
+#define _COMMA_14 ,
+#define _COMMA_15 ,
// 1-based comma separated list of ITEMs
#define COMMA_SEP_LIST(ITEM, LENGTH) _COMMA_SEP_LIST_##LENGTH(ITEM)
+#define _COMMA_SEP_LIST_15(ITEM) \
+ _COMMA_SEP_LIST_14(ITEM) \
+ , ITEM(15)
+#define _COMMA_SEP_LIST_14(ITEM) \
+ _COMMA_SEP_LIST_13(ITEM) \
+ , ITEM(14)
#define _COMMA_SEP_LIST_13(ITEM) \
_COMMA_SEP_LIST_12(ITEM) \
, ITEM(13)
@@ -98,6 +106,12 @@
// 1-based semicolon separated list of ITEMs
#define SEMIC_SEP_LIST(ITEM, LENGTH) _SEMIC_SEP_LIST_##LENGTH(ITEM)
+#define _SEMIC_SEP_LIST_15(ITEM) \
+ _SEMIC_SEP_LIST_14(ITEM); \
+ ITEM(15)
+#define _SEMIC_SEP_LIST_14(ITEM) \
+ _SEMIC_SEP_LIST_13(ITEM); \
+ ITEM(14)
#define _SEMIC_SEP_LIST_13(ITEM) \
_SEMIC_SEP_LIST_12(ITEM); \
ITEM(13)
@@ -141,6 +155,12 @@
// 1-based space separated list of ITEMs
#define SPACE_SEP_LIST(ITEM, LENGTH) _SPACE_SEP_LIST_##LENGTH(ITEM)
+#define _SPACE_SEP_LIST_15(ITEM) \
+ _SPACE_SEP_LIST_14(ITEM) \
+ ITEM(15)
+#define _SPACE_SEP_LIST_14(ITEM) \
+ _SPACE_SEP_LIST_13(ITEM) \
+ ITEM(14)
#define _SPACE_SEP_LIST_13(ITEM) \
_SPACE_SEP_LIST_12(ITEM) \
ITEM(13)
@@ -271,13 +291,13 @@
ss->in_use = false; \
}
-#define MAX_CMD_PARAMS 13
+#define MAX_CMD_PARAMS 15
class CommandQueueMT {
struct SyncSemaphore {
- Semaphore *sem;
+ SemaphoreOld *sem;
bool in_use;
};
@@ -298,15 +318,15 @@ class CommandQueueMT {
};
DECL_CMD(0)
- SPACE_SEP_LIST(DECL_CMD, 13)
+ SPACE_SEP_LIST(DECL_CMD, 15)
/* comands that return */
DECL_CMD_RET(0)
- SPACE_SEP_LIST(DECL_CMD_RET, 13)
+ SPACE_SEP_LIST(DECL_CMD_RET, 15)
/* commands that don't return but sync */
DECL_CMD_SYNC(0)
- SPACE_SEP_LIST(DECL_CMD_SYNC, 13)
+ SPACE_SEP_LIST(DECL_CMD_SYNC, 15)
/***** BASE *******/
@@ -322,7 +342,7 @@ class CommandQueueMT {
uint32_t dealloc_ptr;
SyncSemaphore sync_sems[SYNC_SEMAPHORES];
Mutex *mutex;
- Semaphore *sync;
+ SemaphoreOld *sync;
template <class T>
T *allocate() {
@@ -443,15 +463,15 @@ class CommandQueueMT {
public:
/* NORMAL PUSH COMMANDS */
DECL_PUSH(0)
- SPACE_SEP_LIST(DECL_PUSH, 13)
+ SPACE_SEP_LIST(DECL_PUSH, 15)
/* PUSH AND RET COMMANDS */
DECL_PUSH_AND_RET(0)
- SPACE_SEP_LIST(DECL_PUSH_AND_RET, 13)
+ SPACE_SEP_LIST(DECL_PUSH_AND_RET, 15)
/* PUSH AND RET SYNC COMMANDS*/
DECL_PUSH_AND_SYNC(0)
- SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 13)
+ SPACE_SEP_LIST(DECL_PUSH_AND_SYNC, 15)
void wait_and_flush_one() {
ERR_FAIL_COND(!sync);
diff --git a/core/engine.cpp b/core/engine.cpp
index 1772cc7c48..85ad175f38 100644
--- a/core/engine.cpp
+++ b/core/engine.cpp
@@ -214,6 +214,9 @@ Engine *Engine::get_singleton() {
return singleton;
}
+bool Engine::is_abort_on_gpu_errors_enabled() const {
+ return abort_on_gpu_errors;
+}
Engine::Engine() {
singleton = this;
@@ -232,4 +235,5 @@ Engine::Engine() {
_frame_ticks = 0;
_frame_step = 0;
editor_hint = false;
+ abort_on_gpu_errors = false;
}
diff --git a/core/engine.h b/core/engine.h
index 1aab907ac8..cfe3a918fc 100644
--- a/core/engine.h
+++ b/core/engine.h
@@ -64,6 +64,7 @@ private:
bool _pixel_snap;
uint64_t _physics_frames;
float _physics_interpolation_fraction;
+ bool abort_on_gpu_errors;
uint64_t _idle_frames;
bool _in_physics;
@@ -126,6 +127,8 @@ public:
Dictionary get_license_info() const;
String get_license_text() const;
+ bool is_abort_on_gpu_errors_enabled() const;
+
Engine();
virtual ~Engine() {}
};
diff --git a/core/image.cpp b/core/image.cpp
index 09b07bba13..b126437fa9 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -79,12 +79,16 @@ const char *Image::format_names[Image::FORMAT_MAX] = {
"ETC2_RGB8",
"ETC2_RGBA8",
"ETC2_RGB8A1",
+ "ETC2_RA_AS_RG",
+ "FORMAT_DXT5_RA_AS_RG",
};
SavePNGFunc Image::save_png_func = NULL;
SaveEXRFunc Image::save_exr_func = NULL;
+SavePNGBufferFunc Image::save_png_buffer_func = NULL;
+
void Image::_put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel) {
uint32_t ofs = (p_y * width + p_x) * p_pixelsize;
@@ -115,7 +119,7 @@ int Image::get_format_pixel_size(Format p_format) {
case FORMAT_RGB8: return 3;
case FORMAT_RGBA8: return 4;
case FORMAT_RGBA4444: return 2;
- case FORMAT_RGBA5551: return 2;
+ case FORMAT_RGB565: return 2;
case FORMAT_RF:
return 4; //float
case FORMAT_RGF: return 8;
@@ -159,6 +163,8 @@ int Image::get_format_pixel_size(Format p_format) {
case FORMAT_ETC2_RGB8: return 1;
case FORMAT_ETC2_RGBA8: return 1;
case FORMAT_ETC2_RGB8A1: return 1;
+ case FORMAT_ETC2_RA_AS_RG: return 1;
+ case FORMAT_DXT5_RA_AS_RG: return 1;
case FORMAT_MAX: {
}
}
@@ -207,7 +213,9 @@ void Image::get_format_min_pixel_size(Format p_format, int &r_w, int &r_h) {
case FORMAT_ETC2_RG11S:
case FORMAT_ETC2_RGB8:
case FORMAT_ETC2_RGBA8:
- case FORMAT_ETC2_RGB8A1: {
+ case FORMAT_ETC2_RGB8A1:
+ case FORMAT_ETC2_RA_AS_RG:
+ case FORMAT_DXT5_RA_AS_RG: {
r_w = 4;
r_h = 4;
@@ -268,7 +276,11 @@ int Image::get_format_block_size(Format p_format) {
case FORMAT_ETC2_RG11S:
case FORMAT_ETC2_RGB8:
case FORMAT_ETC2_RGBA8:
- case FORMAT_ETC2_RGB8A1: {
+ case FORMAT_ETC2_RGB8A1:
+ case FORMAT_ETC2_RA_AS_RG: //used to make basis universal happy
+ case FORMAT_DXT5_RA_AS_RG: //used to make basis universal happy
+
+ {
return 4;
}
@@ -318,6 +330,17 @@ int Image::get_mipmap_offset(int p_mipmap) const {
return ofs;
}
+int Image::get_mipmap_byte_size(int p_mipmap) const {
+
+ ERR_FAIL_INDEX_V(p_mipmap, get_mipmap_count() + 1, -1);
+
+ int ofs, w, h;
+ _get_mipmap_offset_and_size(p_mipmap, ofs, w, h);
+ int ofs2;
+ _get_mipmap_offset_and_size(p_mipmap + 1, ofs2, w, h);
+ return ofs2 - ofs;
+}
+
void Image::get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const {
int ofs, w, h;
@@ -891,6 +914,7 @@ void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
ERR_FAIL_COND_MSG(p_height <= 0, "Image height must be greater than 0.");
ERR_FAIL_COND_MSG(p_width > MAX_WIDTH, "Image width cannot be greater than " + itos(MAX_WIDTH) + ".");
ERR_FAIL_COND_MSG(p_height > MAX_HEIGHT, "Image height cannot be greater than " + itos(MAX_HEIGHT) + ".");
+ ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS));
if (p_width == width && p_height == height)
return;
@@ -1215,7 +1239,7 @@ void Image::flip_x() {
}
}
-int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps) {
+int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps, int *r_mm_width, int *r_mm_height) {
int size = 0;
int w = p_width;
@@ -1242,6 +1266,13 @@ int Image::_get_dst_image_size(int p_width, int p_height, Format p_format, int &
size += s;
+ if (r_mm_width) {
+ *r_mm_width = bw;
+ }
+ if (r_mm_height) {
+ *r_mm_height = bh;
+ }
+
if (p_mipmaps >= 0 && mm == p_mipmaps)
break;
@@ -1547,6 +1578,206 @@ Error Image::generate_mipmaps(bool p_renormalize) {
return OK;
}
+Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map) {
+
+ Vector<double> normal_sat_vec; //summed area table
+ double *normal_sat = nullptr; //summed area table for normalmap
+ int normal_w = 0, normal_h = 0;
+
+ ERR_FAIL_COND_V_MSG(p_normal_map.is_null() || p_normal_map->empty(), ERR_INVALID_PARAMETER, "Must provide a valid normalmap for roughness mipmaps");
+
+ Ref<Image> nm = p_normal_map->duplicate();
+ if (nm->is_compressed()) {
+ nm->decompress();
+ }
+
+ normal_w = nm->get_width();
+ normal_h = nm->get_height();
+
+ normal_sat_vec.resize(normal_w * normal_h * 3);
+
+ normal_sat = normal_sat_vec.ptrw();
+
+ //create summed area table
+ nm->lock();
+
+ for (int y = 0; y < normal_h; y++) {
+ double line_sum[3] = { 0, 0, 0 };
+ for (int x = 0; x < normal_w; x++) {
+ double normal[3];
+ Color color = nm->get_pixel(x, y);
+ normal[0] = color.r * 2.0 - 1.0;
+ normal[1] = color.g * 2.0 - 1.0;
+ normal[2] = Math::sqrt(MAX(0.0, 1.0 - (normal[0] * normal[0] + normal[1] * normal[1]))); //reconstruct if missing
+
+ line_sum[0] += normal[0];
+ line_sum[1] += normal[1];
+ line_sum[2] += normal[2];
+
+ uint32_t ofs = (y * normal_w + x) * 3;
+
+ normal_sat[ofs + 0] = line_sum[0];
+ normal_sat[ofs + 1] = line_sum[1];
+ normal_sat[ofs + 2] = line_sum[2];
+
+ if (y > 0) {
+ uint32_t prev_ofs = ((y - 1) * normal_w + x) * 3;
+ normal_sat[ofs + 0] += normal_sat[prev_ofs + 0];
+ normal_sat[ofs + 1] += normal_sat[prev_ofs + 1];
+ normal_sat[ofs + 2] += normal_sat[prev_ofs + 2];
+ }
+ }
+ }
+
+#if 0
+ {
+ Vector3 beg(normal_sat_vec[0], normal_sat_vec[1], normal_sat_vec[2]);
+ Vector3 end(normal_sat_vec[normal_sat_vec.size() - 3], normal_sat_vec[normal_sat_vec.size() - 2], normal_sat_vec[normal_sat_vec.size() - 1]);
+ Vector3 avg = (end - beg) / (normal_w * normal_h);
+ print_line("average: " + avg);
+ }
+#endif
+
+ int mmcount;
+
+ _get_dst_image_size(width, height, format, mmcount);
+
+ lock();
+
+ uint8_t *base_ptr = write_lock.ptr();
+
+ for (int i = 1; i <= mmcount; i++) {
+
+ int ofs, w, h;
+ _get_mipmap_offset_and_size(i, ofs, w, h);
+ uint8_t *ptr = &base_ptr[ofs];
+
+ for (int x = 0; x < w; x++) {
+ for (int y = 0; y < h; y++) {
+ int from_x = x * normal_w / w;
+ int from_y = y * normal_h / h;
+ int to_x = (x + 1) * normal_w / w;
+ int to_y = (y + 1) * normal_h / h;
+ to_x = MIN(to_x - 1, normal_w);
+ to_y = MIN(to_y - 1, normal_h);
+
+ int size_x = (to_x - from_x) + 1;
+ int size_y = (to_y - from_y) + 1;
+
+ //summed area table version (much faster)
+
+ double avg[3] = { 0, 0, 0 };
+
+ if (from_x > 0 && from_y > 0) {
+ uint32_t tofs = ((from_y - 1) * normal_w + (from_x - 1)) * 3;
+ avg[0] += normal_sat[tofs + 0];
+ avg[1] += normal_sat[tofs + 1];
+ avg[2] += normal_sat[tofs + 2];
+ }
+
+ if (from_y > 0) {
+ uint32_t tofs = ((from_y - 1) * normal_w + to_x) * 3;
+ avg[0] -= normal_sat[tofs + 0];
+ avg[1] -= normal_sat[tofs + 1];
+ avg[2] -= normal_sat[tofs + 2];
+ }
+
+ if (from_x > 0) {
+ uint32_t tofs = (to_y * normal_w + (from_x - 1)) * 3;
+ avg[0] -= normal_sat[tofs + 0];
+ avg[1] -= normal_sat[tofs + 1];
+ avg[2] -= normal_sat[tofs + 2];
+ }
+
+ uint32_t tofs = (to_y * normal_w + to_x) * 3;
+ avg[0] += normal_sat[tofs + 0];
+ avg[1] += normal_sat[tofs + 1];
+ avg[2] += normal_sat[tofs + 2];
+
+ double div = double(size_x * size_y);
+ Vector3 vec(avg[0] / div, avg[1] / div, avg[2] / div);
+
+ float r = vec.length();
+
+ int pixel_ofs = y * w + x;
+ Color c = _get_color_at_ofs(ptr, pixel_ofs);
+
+ float roughness;
+
+ switch (p_roughness_channel) {
+ case ROUGHNESS_CHANNEL_R: {
+ roughness = c.r;
+ } break;
+ case ROUGHNESS_CHANNEL_G: {
+ roughness = c.g;
+ } break;
+ case ROUGHNESS_CHANNEL_B: {
+ roughness = c.b;
+ } break;
+ case ROUGHNESS_CHANNEL_L: {
+ roughness = c.gray();
+ } break;
+ case ROUGHNESS_CHANNEL_A: {
+ roughness = c.a;
+ } break;
+ }
+
+ float variance = 0;
+ if (r < 1.0f) {
+ float r2 = r * r;
+ float kappa = (3.0f * r - r * r2) / (1.0f - r2);
+ variance = 0.25f / kappa;
+ }
+
+ float threshold = 0.4;
+ roughness = Math::sqrt(roughness * roughness + MIN(3.0f * variance, threshold * threshold));
+
+ switch (p_roughness_channel) {
+ case ROUGHNESS_CHANNEL_R: {
+ c.r = roughness;
+ } break;
+ case ROUGHNESS_CHANNEL_G: {
+ c.g = roughness;
+ } break;
+ case ROUGHNESS_CHANNEL_B: {
+ c.b = roughness;
+ } break;
+ case ROUGHNESS_CHANNEL_L: {
+ c.r = roughness;
+ c.g = roughness;
+ c.b = roughness;
+ } break;
+ case ROUGHNESS_CHANNEL_A: {
+ c.a = roughness;
+ } break;
+ }
+
+ _set_color_at_ofs(ptr, pixel_ofs, c);
+ }
+ }
+#if 0
+ {
+ int size = get_mipmap_byte_size(i);
+ print_line("size for mimpap " + itos(i) + ": " + itos(size));
+ PoolVector<uint8_t> imgdata;
+ imgdata.resize(size);
+ PoolVector<uint8_t>::Write wr = imgdata.write();
+ copymem(wr.ptr(), ptr, size);
+ wr = PoolVector<uint8_t>::Write();
+ Ref<Image> im;
+ im.instance();
+ im->create(w, h, false, format, imgdata);
+ im->save_png("res://mipmap_" + itos(i) + ".png");
+ }
+#endif
+ }
+
+ unlock();
+ nm->unlock();
+
+ return OK;
+}
+
void Image::clear_mipmaps() {
if (!mipmaps)
@@ -1576,6 +1807,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH);
ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT);
+ ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS));
int mm = 0;
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
@@ -1595,6 +1827,7 @@ void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_forma
ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH);
ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT);
+ ERR_FAIL_COND_MSG(p_width * p_height > MAX_PIXELS, "Too many pixels for image, maximum is " + itos(MAX_PIXELS));
int mm;
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
@@ -1893,6 +2126,14 @@ Error Image::save_png(const String &p_path) const {
return save_png_func(p_path, Ref<Image>((Image *)this));
}
+PoolVector<uint8_t> Image::save_png_to_buffer() const {
+ if (save_png_buffer_func == NULL) {
+ return PoolVector<uint8_t>();
+ }
+
+ return save_png_buffer_func(Ref<Image>((Image *)this));
+}
+
Error Image::save_exr(const String &p_path, bool p_grayscale) const {
if (save_exr_func == NULL)
@@ -1914,6 +2155,13 @@ int Image::get_image_required_mipmaps(int p_width, int p_height, Format p_format
return mm;
}
+Size2i Image::get_image_mipmap_size(int p_width, int p_height, Format p_format, int p_mipmap) {
+ int mm;
+ Size2i ret;
+ _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmap, &ret.x, &ret.y);
+ return ret;
+}
+
int Image::get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap) {
if (p_mipmap <= 0) {
@@ -1923,13 +2171,24 @@ int Image::get_image_mipmap_offset(int p_width, int p_height, Format p_format, i
return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmap - 1);
}
+int Image::get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h) {
+
+ if (p_mipmap <= 0) {
+ r_w = p_width;
+ r_h = p_height;
+ return 0;
+ }
+ int mm;
+ return _get_dst_image_size(p_width, p_height, p_format, mm, p_mipmap - 1, &r_w, &r_h);
+}
+
bool Image::is_compressed() const {
return format > FORMAT_RGBE9995;
}
Error Image::decompress() {
- if (format >= FORMAT_DXT1 && format <= FORMAT_RGTC_RG && _image_decompress_bc)
+ if (((format >= FORMAT_DXT1 && format <= FORMAT_RGTC_RG) || (format == FORMAT_DXT5_RA_AS_RG)) && _image_decompress_bc)
_image_decompress_bc(this);
else if (format >= FORMAT_BPTC_RGBA && format <= FORMAT_BPTC_RGBFU && _image_decompress_bptc)
_image_decompress_bptc(this);
@@ -1937,7 +2196,7 @@ Error Image::decompress() {
_image_decompress_pvrtc(this);
else if (format == FORMAT_ETC && _image_decompress_etc1)
_image_decompress_etc1(this);
- else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RGB8A1 && _image_decompress_etc2)
+ else if (format >= FORMAT_ETC2_R11 && format <= FORMAT_ETC2_RA_AS_RG && _image_decompress_etc2)
_image_decompress_etc2(this);
else
return ERR_UNAVAILABLE;
@@ -1946,12 +2205,16 @@ Error Image::decompress() {
Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_lossy_quality) {
+ return compress_from_channels(p_mode, detect_used_channels(p_source), p_lossy_quality);
+}
+Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality) {
+
switch (p_mode) {
case COMPRESS_S3TC: {
ERR_FAIL_COND_V(!_image_compress_bc_func, ERR_UNAVAILABLE);
- _image_compress_bc_func(this, p_lossy_quality, p_source);
+ _image_compress_bc_func(this, p_lossy_quality, p_channels);
} break;
case COMPRESS_PVRTC2: {
@@ -1971,12 +2234,12 @@ Error Image::compress(CompressMode p_mode, CompressSource p_source, float p_loss
case COMPRESS_ETC2: {
ERR_FAIL_COND_V(!_image_compress_etc2_func, ERR_UNAVAILABLE);
- _image_compress_etc2_func(this, p_lossy_quality, p_source);
+ _image_compress_etc2_func(this, p_lossy_quality, p_channels);
} break;
case COMPRESS_BPTC: {
ERR_FAIL_COND_V(!_image_compress_bptc_func, ERR_UNAVAILABLE);
- _image_compress_bptc_func(this, p_lossy_quality, p_source);
+ _image_compress_bptc_func(this, p_lossy_quality, p_channels);
} break;
}
@@ -2015,7 +2278,7 @@ Image::Image(int p_width, int p_height, bool p_mipmaps, Format p_format, const P
Rect2 Image::get_used_rect() const {
- if (format != FORMAT_LA8 && format != FORMAT_RGBA8 && format != FORMAT_RGBAF && format != FORMAT_RGBAH && format != FORMAT_RGBA4444 && format != FORMAT_RGBA5551)
+ if (format != FORMAT_LA8 && format != FORMAT_RGBA8 && format != FORMAT_RGBAF && format != FORMAT_RGBAH && format != FORMAT_RGBA4444 && format != FORMAT_RGB565)
return Rect2(Point2(), Size2(width, height));
int len = data.size();
@@ -2317,12 +2580,12 @@ ImageMemLoadFunc Image::_png_mem_loader_func = NULL;
ImageMemLoadFunc Image::_jpg_mem_loader_func = NULL;
ImageMemLoadFunc Image::_webp_mem_loader_func = NULL;
-void (*Image::_image_compress_bc_func)(Image *, float, Image::CompressSource) = NULL;
-void (*Image::_image_compress_bptc_func)(Image *, float, Image::CompressSource) = NULL;
+void (*Image::_image_compress_bc_func)(Image *, float, Image::UsedChannels) = NULL;
+void (*Image::_image_compress_bptc_func)(Image *, float, Image::UsedChannels) = NULL;
void (*Image::_image_compress_pvrtc2_func)(Image *) = NULL;
void (*Image::_image_compress_pvrtc4_func)(Image *) = NULL;
void (*Image::_image_compress_etc1_func)(Image *, float) = NULL;
-void (*Image::_image_compress_etc2_func)(Image *, float, Image::CompressSource) = NULL;
+void (*Image::_image_compress_etc2_func)(Image *, float, Image::UsedChannels) = NULL;
void (*Image::_image_decompress_pvrtc)(Image *) = NULL;
void (*Image::_image_decompress_bc)(Image *) = NULL;
void (*Image::_image_decompress_bptc)(Image *) = NULL;
@@ -2333,6 +2596,8 @@ PoolVector<uint8_t> (*Image::lossy_packer)(const Ref<Image> &, float) = NULL;
Ref<Image> (*Image::lossy_unpacker)(const PoolVector<uint8_t> &) = NULL;
PoolVector<uint8_t> (*Image::lossless_packer)(const Ref<Image> &) = NULL;
Ref<Image> (*Image::lossless_unpacker)(const PoolVector<uint8_t> &) = NULL;
+PoolVector<uint8_t> (*Image::basis_universal_packer)(const Ref<Image> &, Image::UsedChannels) = NULL;
+Ref<Image> (*Image::basis_universal_unpacker)(const PoolVector<uint8_t> &) = NULL;
void Image::_set_data(const Dictionary &p_data) {
@@ -2386,18 +2651,7 @@ Color Image::get_pixelv(const Point2 &p_src) const {
return get_pixel(p_src.x, p_src.y);
}
-Color Image::get_pixel(int p_x, int p_y) const {
-
- uint8_t *ptr = write_lock.ptr();
-#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_V_MSG(!ptr, Color(), "Image must be locked with 'lock()' before using get_pixel().");
-
- ERR_FAIL_INDEX_V(p_x, width, Color());
- ERR_FAIL_INDEX_V(p_y, height, Color());
-
-#endif
-
- uint32_t ofs = p_y * width + p_x;
+Color Image::_get_color_at_ofs(uint8_t *ptr, uint32_t ofs) const {
switch (format) {
case FORMAT_L8: {
@@ -2441,14 +2695,13 @@ Color Image::get_pixel(int p_x, int p_y) const {
float a = (u & 0xF) / 15.0;
return Color(r, g, b, a);
}
- case FORMAT_RGBA5551: {
+ case FORMAT_RGB565: {
uint16_t u = ((uint16_t *)ptr)[ofs];
- float r = ((u >> 11) & 0x1F) / 15.0;
- float g = ((u >> 6) & 0x1F) / 15.0;
- float b = ((u >> 1) & 0x1F) / 15.0;
- float a = (u & 0x1) / 1.0;
- return Color(r, g, b, a);
+ float r = (u & 0x1F) / 31.0;
+ float g = ((u >> 5) & 0x3F) / 63.0;
+ float b = ((u >> 11) & 0x1F) / 31.0;
+ return Color(r, g, b, 1.0);
}
case FORMAT_RF: {
@@ -2511,23 +2764,7 @@ Color Image::get_pixel(int p_x, int p_y) const {
}
}
-void Image::set_pixelv(const Point2 &p_dst, const Color &p_color) {
- set_pixel(p_dst.x, p_dst.y, p_color);
-}
-
-void Image::set_pixel(int p_x, int p_y, const Color &p_color) {
-
- uint8_t *ptr = write_lock.ptr();
-#ifdef DEBUG_ENABLED
- ERR_FAIL_COND_MSG(!ptr, "Image must be locked with 'lock()' before using set_pixel().");
-
- ERR_FAIL_INDEX(p_x, width);
- ERR_FAIL_INDEX(p_y, height);
-
-#endif
-
- uint32_t ofs = p_y * width + p_x;
-
+void Image::_set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color) {
switch (format) {
case FORMAT_L8: {
ptr[ofs] = uint8_t(CLAMP(p_color.get_v() * 255.0, 0, 255));
@@ -2569,14 +2806,13 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) {
((uint16_t *)ptr)[ofs] = rgba;
} break;
- case FORMAT_RGBA5551: {
+ case FORMAT_RGB565: {
uint16_t rgba = 0;
- rgba = uint16_t(CLAMP(p_color.r * 31.0, 0, 31)) << 11;
- rgba |= uint16_t(CLAMP(p_color.g * 31.0, 0, 31)) << 6;
- rgba |= uint16_t(CLAMP(p_color.b * 31.0, 0, 31)) << 1;
- rgba |= uint16_t(p_color.a > 0.5 ? 1 : 0);
+ rgba = uint16_t(CLAMP(p_color.r * 31.0, 0, 31));
+ rgba |= uint16_t(CLAMP(p_color.g * 63.0, 0, 33)) << 5;
+ rgba |= uint16_t(CLAMP(p_color.b * 31.0, 0, 31)) << 11;
((uint16_t *)ptr)[ofs] = rgba;
@@ -2636,10 +2872,44 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) {
}
}
-Image::DetectChannels Image::get_detected_channels() {
+Color Image::get_pixel(int p_x, int p_y) const {
+
+ uint8_t *ptr = write_lock.ptr();
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_V_MSG(!ptr, Color(), "Image must be locked with 'lock()' before using get_pixel().");
+
+ ERR_FAIL_INDEX_V(p_x, width, Color());
+ ERR_FAIL_INDEX_V(p_y, height, Color());
+
+#endif
+
+ uint32_t ofs = p_y * width + p_x;
+ return _get_color_at_ofs(ptr, ofs);
+}
+
+void Image::set_pixelv(const Point2 &p_dst, const Color &p_color) {
+ set_pixel(p_dst.x, p_dst.y, p_color);
+}
+
+void Image::set_pixel(int p_x, int p_y, const Color &p_color) {
+
+ uint8_t *ptr = write_lock.ptr();
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND_MSG(!ptr, "Image must be locked with 'lock()' before using set_pixel().");
+
+ ERR_FAIL_INDEX(p_x, width);
+ ERR_FAIL_INDEX(p_y, height);
+
+#endif
+
+ uint32_t ofs = p_y * width + p_x;
+ _set_color_at_ofs(ptr, ofs, p_color);
+}
- ERR_FAIL_COND_V(data.size() == 0, DETECTED_RGBA);
- ERR_FAIL_COND_V(is_compressed(), DETECTED_RGBA);
+Image::UsedChannels Image::detect_used_channels(CompressSource p_source) {
+
+ ERR_FAIL_COND_V(data.size() == 0, USED_CHANNELS_RGBA);
+ ERR_FAIL_COND_V(is_compressed(), USED_CHANNELS_RGBA);
bool r = false, g = false, b = false, a = false, c = false;
lock();
for (int i = 0; i < width; i++) {
@@ -2664,31 +2934,42 @@ Image::DetectChannels Image::get_detected_channels() {
unlock();
- if (!c && !a)
- return DETECTED_L;
- if (!c && a)
- return DETECTED_LA;
+ UsedChannels used_channels;
- if (r && !g && !b && !a)
- return DETECTED_R;
+ if (!c && !a)
+ used_channels = USED_CHANNELS_L;
+ else if (!c && a)
+ used_channels = USED_CHANNELS_LA;
+ else if (r && !g && !b && !a)
+ used_channels = USED_CHANNELS_R;
+ else if (r && g && !b && !a)
+ used_channels = USED_CHANNELS_RG;
+ else if (r && g && b && !a)
+ used_channels = USED_CHANNELS_RGB;
+ else
+ used_channels = USED_CHANNELS_RGBA;
- if (r && g && !b && !a)
- return DETECTED_RG;
+ if (p_source == COMPRESS_SOURCE_SRGB && (used_channels == USED_CHANNELS_R || used_channels == USED_CHANNELS_RG)) {
+ //R and RG do not support SRGB
+ used_channels = USED_CHANNELS_RGB;
+ }
- if (r && g && b && !a)
- return DETECTED_RGB;
+ if (p_source == COMPRESS_SOURCE_NORMAL) {
+ //use RG channels only for normal
+ used_channels = USED_CHANNELS_RG;
+ }
- return DETECTED_RGBA;
+ return used_channels;
}
void Image::optimize_channels() {
- switch (get_detected_channels()) {
- case DETECTED_L: convert(FORMAT_L8); break;
- case DETECTED_LA: convert(FORMAT_LA8); break;
- case DETECTED_R: convert(FORMAT_R8); break;
- case DETECTED_RG: convert(FORMAT_RG8); break;
- case DETECTED_RGB: convert(FORMAT_RGB8); break;
- case DETECTED_RGBA: convert(FORMAT_RGBA8); break;
+ switch (detect_used_channels()) {
+ case USED_CHANNELS_L: convert(FORMAT_L8); break;
+ case USED_CHANNELS_LA: convert(FORMAT_LA8); break;
+ case USED_CHANNELS_R: convert(FORMAT_R8); break;
+ case USED_CHANNELS_RG: convert(FORMAT_RG8); break;
+ case USED_CHANNELS_RGB: convert(FORMAT_RGB8); break;
+ case USED_CHANNELS_RGBA: convert(FORMAT_RGBA8); break;
}
}
@@ -2728,7 +3009,9 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("detect_alpha"), &Image::detect_alpha);
ClassDB::bind_method(D_METHOD("is_invisible"), &Image::is_invisible);
- ClassDB::bind_method(D_METHOD("compress", "mode", "source", "lossy_quality"), &Image::compress);
+ ClassDB::bind_method(D_METHOD("detect_used_channels", "source"), &Image::detect_used_channels, DEFVAL(COMPRESS_SOURCE_GENERIC));
+ ClassDB::bind_method(D_METHOD("compress", "mode", "source", "lossy_quality"), &Image::compress, DEFVAL(COMPRESS_SOURCE_GENERIC), DEFVAL(0.7));
+ ClassDB::bind_method(D_METHOD("compress_from_channels", "mode", "channels", "lossy_quality"), &Image::compress, DEFVAL(0.7));
ClassDB::bind_method(D_METHOD("decompress"), &Image::decompress);
ClassDB::bind_method(D_METHOD("is_compressed"), &Image::is_compressed);
@@ -2776,7 +3059,7 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(FORMAT_RGB8);
BIND_ENUM_CONSTANT(FORMAT_RGBA8);
BIND_ENUM_CONSTANT(FORMAT_RGBA4444);
- BIND_ENUM_CONSTANT(FORMAT_RGBA5551);
+ BIND_ENUM_CONSTANT(FORMAT_RGB565);
BIND_ENUM_CONSTANT(FORMAT_RF); //float
BIND_ENUM_CONSTANT(FORMAT_RGF);
BIND_ENUM_CONSTANT(FORMAT_RGBF);
@@ -2806,6 +3089,8 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(FORMAT_ETC2_RGB8);
BIND_ENUM_CONSTANT(FORMAT_ETC2_RGBA8);
BIND_ENUM_CONSTANT(FORMAT_ETC2_RGB8A1);
+ BIND_ENUM_CONSTANT(FORMAT_ETC2_RA_AS_RG);
+ BIND_ENUM_CONSTANT(FORMAT_DXT5_RA_AS_RG);
BIND_ENUM_CONSTANT(FORMAT_MAX);
BIND_ENUM_CONSTANT(INTERPOLATE_NEAREST);
@@ -2824,17 +3109,24 @@ void Image::_bind_methods() {
BIND_ENUM_CONSTANT(COMPRESS_ETC);
BIND_ENUM_CONSTANT(COMPRESS_ETC2);
+ BIND_ENUM_CONSTANT(USED_CHANNELS_L);
+ BIND_ENUM_CONSTANT(USED_CHANNELS_LA);
+ BIND_ENUM_CONSTANT(USED_CHANNELS_R);
+ BIND_ENUM_CONSTANT(USED_CHANNELS_RG);
+ BIND_ENUM_CONSTANT(USED_CHANNELS_RGB);
+ BIND_ENUM_CONSTANT(USED_CHANNELS_RGBA);
+
BIND_ENUM_CONSTANT(COMPRESS_SOURCE_GENERIC);
BIND_ENUM_CONSTANT(COMPRESS_SOURCE_SRGB);
BIND_ENUM_CONSTANT(COMPRESS_SOURCE_NORMAL);
}
-void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, CompressSource)) {
+void Image::set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels)) {
_image_compress_bc_func = p_compress_func;
}
-void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, float, CompressSource)) {
+void Image::set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels)) {
_image_compress_bptc_func = p_compress_func;
}
@@ -2890,6 +3182,29 @@ Ref<Image> Image::rgbe_to_srgb() {
return new_image;
}
+Ref<Image> Image::get_image_from_mipmap(int p_mipamp) const {
+
+ int ofs, size, w, h;
+ get_mipmap_offset_size_and_dimensions(p_mipamp, ofs, size, w, h);
+
+ PoolVector<uint8_t> new_data;
+ new_data.resize(size);
+ {
+ PoolVector<uint8_t>::Write wr = new_data.write();
+ PoolVector<uint8_t>::Read rd = data.read();
+ copymem(wr.ptr(), rd.ptr() + ofs, size);
+ }
+
+ Ref<Image> image;
+ image.instance();
+ image->width = w;
+ image->height = h;
+ image->format = format;
+ image->data = new_data;
+ image->mipmaps = false;
+ return image;
+}
+
void Image::bumpmap_to_normalmap(float bump_scale) {
ERR_FAIL_COND(!_can_modify(format));
convert(Image::FORMAT_RF);
@@ -3078,6 +3393,31 @@ Error Image::load_webp_from_buffer(const PoolVector<uint8_t> &p_array) {
return _load_from_buffer(p_array, _webp_mem_loader_func);
}
+void Image::convert_rg_to_ra_rgba8() {
+ ERR_FAIL_COND(format != FORMAT_RGBA8);
+ ERR_FAIL_COND(!data.size());
+
+ int s = data.size();
+ PoolVector<uint8_t>::Write w = data.write();
+ for (int i = 0; i < s; i += 4) {
+ w[i + 3] = w[i + 1];
+ w[i + 1] = 0;
+ w[i + 2] = 0;
+ }
+}
+void Image::convert_ra_rgba8_to_rg() {
+ ERR_FAIL_COND(format != FORMAT_RGBA8);
+ ERR_FAIL_COND(!data.size());
+
+ int s = data.size();
+ PoolVector<uint8_t>::Write w = data.write();
+ for (int i = 0; i < s; i += 4) {
+ w[i + 1] = w[i + 3];
+ w[i + 2] = 0;
+ w[i + 3] = 255;
+ }
+}
+
Error Image::_load_from_buffer(const PoolVector<uint8_t> &p_array, ImageMemLoadFunc p_loader) {
int buffer_size = p_array.size();
diff --git a/core/image.h b/core/image.h
index c15cfc9f6f..1cc22420d5 100644
--- a/core/image.h
+++ b/core/image.h
@@ -47,6 +47,7 @@
class Image;
typedef Error (*SavePNGFunc)(const String &p_path, const Ref<Image> &p_img);
+typedef PoolVector<uint8_t> (*SavePNGBufferFunc)(const Ref<Image> &p_img);
typedef Ref<Image> (*ImageMemLoadFunc)(const uint8_t *p_png, int p_size);
typedef Error (*SaveEXRFunc)(const String &p_path, const Ref<Image> &p_img, bool p_grayscale);
@@ -57,10 +58,12 @@ class Image : public Resource {
public:
static SavePNGFunc save_png_func;
static SaveEXRFunc save_exr_func;
+ static SavePNGBufferFunc save_png_buffer_func;
enum {
- MAX_WIDTH = 16384, // force a limit somehow
- MAX_HEIGHT = 16384 // force a limit somehow
+ MAX_WIDTH = (1 << 24), // force a limit somehow
+ MAX_HEIGHT = (1 << 24), // force a limit somehow
+ MAX_PIXELS = 268435456
};
enum Format {
@@ -72,7 +75,7 @@ public:
FORMAT_RGB8,
FORMAT_RGBA8,
FORMAT_RGBA4444,
- FORMAT_RGBA5551,
+ FORMAT_RGB565,
FORMAT_RF, //float
FORMAT_RGF,
FORMAT_RGBF,
@@ -102,6 +105,8 @@ public:
FORMAT_ETC2_RGB8,
FORMAT_ETC2_RGBA8,
FORMAT_ETC2_RGB8A1,
+ FORMAT_ETC2_RA_AS_RG, //used to make basis universal happy
+ FORMAT_DXT5_RA_AS_RG, //used to make basis universal happy
FORMAT_MAX
};
@@ -117,25 +122,27 @@ public:
/* INTERPOLATE GAUSS */
};
- enum CompressSource {
- COMPRESS_SOURCE_GENERIC,
- COMPRESS_SOURCE_SRGB,
- COMPRESS_SOURCE_NORMAL,
- COMPRESS_SOURCE_LAYERED,
+ //this is used for compression
+ enum UsedChannels {
+ USED_CHANNELS_L,
+ USED_CHANNELS_LA,
+ USED_CHANNELS_R,
+ USED_CHANNELS_RG,
+ USED_CHANNELS_RGB,
+ USED_CHANNELS_RGBA,
};
-
//some functions provided by something else
static ImageMemLoadFunc _png_mem_loader_func;
static ImageMemLoadFunc _jpg_mem_loader_func;
static ImageMemLoadFunc _webp_mem_loader_func;
- static void (*_image_compress_bc_func)(Image *, float, CompressSource p_source);
- static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, CompressSource p_source);
+ static void (*_image_compress_bc_func)(Image *, float, UsedChannels p_channels);
+ static void (*_image_compress_bptc_func)(Image *, float p_lossy_quality, UsedChannels p_channels);
static void (*_image_compress_pvrtc2_func)(Image *);
static void (*_image_compress_pvrtc4_func)(Image *);
static void (*_image_compress_etc1_func)(Image *, float);
- static void (*_image_compress_etc2_func)(Image *, float, CompressSource p_source);
+ static void (*_image_compress_etc2_func)(Image *, float, UsedChannels p_channels);
static void (*_image_decompress_pvrtc)(Image *);
static void (*_image_decompress_bc)(Image *);
@@ -147,9 +154,14 @@ public:
static Ref<Image> (*lossy_unpacker)(const PoolVector<uint8_t> &p_buffer);
static PoolVector<uint8_t> (*lossless_packer)(const Ref<Image> &p_image);
static Ref<Image> (*lossless_unpacker)(const PoolVector<uint8_t> &p_buffer);
+ static PoolVector<uint8_t> (*basis_universal_packer)(const Ref<Image> &p_image, UsedChannels p_channels);
+ static Ref<Image> (*basis_universal_unpacker)(const PoolVector<uint8_t> &p_buffer);
PoolVector<uint8_t>::Write write_lock;
+ _FORCE_INLINE_ Color _get_color_at_ofs(uint8_t *ptr, uint32_t ofs) const;
+ _FORCE_INLINE_ void _set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color);
+
protected:
static void _bind_methods();
@@ -177,7 +189,7 @@ private:
_FORCE_INLINE_ void _get_mipmap_offset_and_size(int p_mipmap, int &r_offset, int &r_width, int &r_height) const; //get where the mipmap begins in data
- static int _get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps = -1);
+ static int _get_dst_image_size(int p_width, int p_height, Format p_format, int &r_mipmaps, int p_mipmaps = -1, int *r_mm_width = NULL, int *r_mm_height = NULL);
bool _can_modify(Format p_format) const;
_FORCE_INLINE_ void _put_pixelb(int p_x, int p_y, uint32_t p_pixelsize, uint8_t *p_data, const uint8_t *p_pixel);
@@ -214,6 +226,7 @@ public:
*/
Format get_format() const;
+ int get_mipmap_byte_size(int p_mipmap) const; //get where the mipmap begins in data
int get_mipmap_offset(int p_mipmap) const; //get where the mipmap begins in data
void get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const; //get where the mipmap begins in data
void get_mipmap_offset_size_and_dimensions(int p_mipmap, int &r_ofs, int &r_size, int &w, int &h) const; //get where the mipmap begins in data
@@ -240,6 +253,16 @@ public:
*/
Error generate_mipmaps(bool p_renormalize = false);
+ enum RoughnessChannel {
+ ROUGHNESS_CHANNEL_R,
+ ROUGHNESS_CHANNEL_G,
+ ROUGHNESS_CHANNEL_B,
+ ROUGHNESS_CHANNEL_A,
+ ROUGHNESS_CHANNEL_L,
+ };
+
+ Error generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map);
+
void clear_mipmaps();
void normalize(); //for normal maps
@@ -259,6 +282,7 @@ public:
Error load(const String &p_path);
Error save_png(const String &p_path) const;
+ PoolVector<uint8_t> save_png_to_buffer() const;
Error save_exr(const String &p_path, bool p_grayscale) const;
/**
@@ -290,7 +314,9 @@ public:
static int get_image_data_size(int p_width, int p_height, Format p_format, bool p_mipmaps = false);
static int get_image_required_mipmaps(int p_width, int p_height, Format p_format);
+ static Size2i get_image_mipmap_size(int p_width, int p_height, Format p_format, int p_mipmap);
static int get_image_mipmap_offset(int p_width, int p_height, Format p_format, int p_mipmap);
+ static int get_image_mipmap_offset_and_dimensions(int p_width, int p_height, Format p_format, int p_mipmap, int &r_w, int &r_h);
enum CompressMode {
COMPRESS_S3TC,
@@ -300,8 +326,14 @@ public:
COMPRESS_ETC2,
COMPRESS_BPTC
};
+ enum CompressSource {
+ COMPRESS_SOURCE_GENERIC,
+ COMPRESS_SOURCE_SRGB,
+ COMPRESS_SOURCE_NORMAL
+ };
- Error compress(CompressMode p_mode = COMPRESS_S3TC, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7);
+ Error compress(CompressMode p_mode, CompressSource p_source = COMPRESS_SOURCE_GENERIC, float p_lossy_quality = 0.7);
+ Error compress_from_channels(CompressMode p_mode, UsedChannels p_channels, float p_lossy_quality = 0.7);
Error decompress();
bool is_compressed() const;
@@ -310,6 +342,7 @@ public:
void srgb_to_linear();
void normalmap_to_xy();
Ref<Image> rgbe_to_srgb();
+ Ref<Image> get_image_from_mipmap(int p_mipamp) const;
void bumpmap_to_normalmap(float bump_scale = 1.0);
void blit_rect(const Ref<Image> &p_src, const Rect2 &p_src_rect, const Point2 &p_dest);
@@ -321,14 +354,17 @@ public:
Rect2 get_used_rect() const;
Ref<Image> get_rect(const Rect2 &p_area) const;
- static void set_compress_bc_func(void (*p_compress_func)(Image *, float, CompressSource));
- static void set_compress_bptc_func(void (*p_compress_func)(Image *, float, CompressSource));
+ static void set_compress_bc_func(void (*p_compress_func)(Image *, float, UsedChannels));
+ static void set_compress_bptc_func(void (*p_compress_func)(Image *, float, UsedChannels));
static String get_format_name(Format p_format);
Error load_png_from_buffer(const PoolVector<uint8_t> &p_array);
Error load_jpg_from_buffer(const PoolVector<uint8_t> &p_array);
Error load_webp_from_buffer(const PoolVector<uint8_t> &p_array);
+ void convert_rg_to_ra_rgba8();
+ void convert_ra_rgba8_to_rg();
+
Image(const uint8_t *p_mem_png_jpg, int p_len = -1);
Image(const char **p_xpm);
@@ -337,17 +373,7 @@ public:
void lock();
void unlock();
- //this is used for compression
- enum DetectChannels {
- DETECTED_L,
- DETECTED_LA,
- DETECTED_R,
- DETECTED_RG,
- DETECTED_RGB,
- DETECTED_RGBA,
- };
-
- DetectChannels get_detected_channels();
+ UsedChannels detect_used_channels(CompressSource p_source = COMPRESS_SOURCE_GENERIC);
void optimize_channels();
Color get_pixelv(const Point2 &p_src) const;
@@ -371,6 +397,8 @@ VARIANT_ENUM_CAST(Image::Format)
VARIANT_ENUM_CAST(Image::Interpolation)
VARIANT_ENUM_CAST(Image::CompressMode)
VARIANT_ENUM_CAST(Image::CompressSource)
+VARIANT_ENUM_CAST(Image::UsedChannels)
VARIANT_ENUM_CAST(Image::AlphaMode)
+VARIANT_ENUM_CAST(Image::RoughnessChannel)
#endif
diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp
index 74e85d0cc0..5968972143 100644
--- a/core/io/config_file.cpp
+++ b/core/io/config_file.cpp
@@ -86,9 +86,10 @@ void ConfigFile::set_value(const String &p_section, const String &p_key, const V
Variant ConfigFile::get_value(const String &p_section, const String &p_key, Variant p_default) const {
if (!values.has(p_section) || !values[p_section].has(p_key)) {
- ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, p_default, "Couldn't find the given section/key and no default was given.");
+ ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, Variant(), "Couldn't find the given section '" + p_section + "', key '" + p_key + "' and no default was given.");
return p_default;
}
+
return values[p_section][p_key];
}
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp
index 1b09ac7208..202eb89dbd 100644
--- a/core/io/file_access_network.cpp
+++ b/core/io/file_access_network.cpp
@@ -231,7 +231,7 @@ FileAccessNetworkClient::FileAccessNetworkClient() {
singleton = this;
last_id = 0;
client.instance();
- sem = Semaphore::create();
+ sem = SemaphoreOld::create();
lockcount = 0;
}
@@ -522,8 +522,8 @@ FileAccessNetwork::FileAccessNetwork() {
eof_flag = false;
opened = false;
pos = 0;
- sem = Semaphore::create();
- page_sem = Semaphore::create();
+ sem = SemaphoreOld::create();
+ page_sem = SemaphoreOld::create();
buffer_mutex = Mutex::create();
FileAccessNetworkClient *nc = FileAccessNetworkClient::singleton;
nc->lock_mutex();
diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h
index e2da1d0893..f329abf7c5 100644
--- a/core/io/file_access_network.h
+++ b/core/io/file_access_network.h
@@ -49,7 +49,7 @@ class FileAccessNetworkClient {
List<BlockRequest> block_requests;
- Semaphore *sem;
+ SemaphoreOld *sem;
Thread *thread;
bool quit;
Mutex *mutex;
@@ -85,8 +85,8 @@ public:
class FileAccessNetwork : public FileAccess {
- Semaphore *sem;
- Semaphore *page_sem;
+ SemaphoreOld *sem;
+ SemaphoreOld *page_sem;
Mutex *buffer_mutex;
bool opened;
size_t total_size;
diff --git a/core/io/ip.cpp b/core/io/ip.cpp
index 23f6ca25d0..7d18117711 100644
--- a/core/io/ip.cpp
+++ b/core/io/ip.cpp
@@ -71,7 +71,7 @@ struct _IP_ResolverPrivate {
}
Mutex *mutex;
- Semaphore *sem;
+ SemaphoreOld *sem;
Thread *thread;
//Semaphore* semaphore;
@@ -319,7 +319,7 @@ IP::IP() {
#ifndef NO_THREADS
- resolver->sem = Semaphore::create();
+ resolver->sem = SemaphoreOld::create();
if (resolver->sem) {
resolver->thread_abort = false;
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 97dca98185..7325532b6f 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -1255,7 +1255,7 @@ String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const
ria->res_path = ria->local_path;
//ria->set_local_path( Globals::get_singleton()->localize_path(p_path) );
String r = ria->recognize(f);
- return r;
+ return ClassDB::get_compatibility_remapped_class(r);
}
///////////////////////////////////////////////////////////
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 0e1ada9475..0ee6478fa2 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -404,6 +404,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
if (!p_no_cache) {
_remove_from_loading_map(local_path);
}
+ print_verbose("Failed loading resource: " + path);
return RES();
}
if (!p_no_cache)
@@ -728,8 +729,9 @@ String ResourceLoader::get_resource_type(const String &p_path) {
for (int i = 0; i < loader_count; i++) {
String result = loader[i]->get_resource_type(local_path);
- if (result != "")
+ if (result != "") {
return result;
+ }
}
return "";
diff --git a/core/make_binders.py b/core/make_binders.py
index c38db5cef4..11cfbf6e79 100644
--- a/core/make_binders.py
+++ b/core/make_binders.py
@@ -342,7 +342,7 @@ def make_version(template, nargs, argmax, const, ret):
def run(target, source, env):
- versions = 13
+ versions = 15
versions_ext = 6
text = ""
text_ext = ""
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index ddf5f13d55..14079f811d 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -244,6 +244,18 @@ void Basis::scale_local(const Vector3 &p_scale) {
*this = scaled_local(p_scale);
}
+float Basis::get_uniform_scale() const {
+ return (elements[0].length() + elements[1].length() + elements[2].length()) / 3.0;
+}
+
+void Basis::make_scale_uniform() {
+ float l = (elements[0].length() + elements[1].length() + elements[2].length()) / 3.0;
+ for (int i = 0; i < 3; i++) {
+ elements[i].normalize();
+ elements[i] *= l;
+ }
+}
+
Basis Basis::scaled_local(const Vector3 &p_scale) const {
Basis b;
b.set_diagonal(p_scale);
diff --git a/core/math/basis.h b/core/math/basis.h
index 6c3a939d70..0261cf67c6 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -108,6 +108,9 @@ public:
void scale_local(const Vector3 &p_scale);
Basis scaled_local(const Vector3 &p_scale) const;
+ void make_scale_uniform();
+ float get_uniform_scale() const;
+
Vector3 get_scale() const;
Vector3 get_scale_abs() const;
Vector3 get_scale_local() const;
diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp
index 380bae871a..c4981b954b 100644
--- a/core/math/camera_matrix.cpp
+++ b/core/math/camera_matrix.cpp
@@ -276,6 +276,36 @@ Vector2 CameraMatrix::get_viewport_half_extents() const {
return Vector2(res.x, res.y);
}
+void CameraMatrix::get_far_plane_size(real_t &r_width, real_t &r_height) const {
+
+ const real_t *matrix = (const real_t *)this->matrix;
+ ///////--- Far Plane ---///////
+ Plane far_plane = Plane(matrix[3] - matrix[2],
+ matrix[7] - matrix[6],
+ matrix[11] - matrix[10],
+ -matrix[15] + matrix[14]);
+ far_plane.normalize();
+
+ ///////--- Right Plane ---///////
+ Plane right_plane = Plane(matrix[3] - matrix[0],
+ matrix[7] - matrix[4],
+ matrix[11] - matrix[8],
+ -matrix[15] + matrix[12]);
+ right_plane.normalize();
+
+ Plane top_plane = Plane(matrix[3] - matrix[1],
+ matrix[7] - matrix[5],
+ matrix[11] - matrix[9],
+ -matrix[15] + matrix[13]);
+ top_plane.normalize();
+
+ Vector3 res;
+ far_plane.intersect_3(right_plane, top_plane, &res);
+
+ r_width = res.x;
+ r_height = res.y;
+}
+
bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8points) const {
Vector<Plane> planes = get_projection_planes(Transform());
@@ -485,6 +515,12 @@ void CameraMatrix::invert() {
}
}
+void CameraMatrix::flip_y() {
+ for (int i = 0; i < 4; i++) {
+ matrix[1][i] = -matrix[1][i];
+ }
+}
+
CameraMatrix::CameraMatrix() {
set_identity();
@@ -506,6 +542,28 @@ CameraMatrix CameraMatrix::operator*(const CameraMatrix &p_matrix) const {
return new_matrix;
}
+void CameraMatrix::set_depth_correction(bool p_flip_y) {
+
+ real_t *m = &matrix[0][0];
+
+ m[0] = 1;
+ m[1] = 0.0;
+ m[2] = 0.0;
+ m[3] = 0.0;
+ m[4] = 0.0;
+ m[5] = p_flip_y ? -1 : 1;
+ m[6] = 0.0;
+ m[7] = 0.0;
+ m[8] = 0.0;
+ m[9] = 0.0;
+ m[10] = 0.5;
+ m[11] = 0.0;
+ m[12] = 0.0;
+ m[13] = 0.0;
+ m[14] = 0.5;
+ m[15] = 1.0;
+}
+
void CameraMatrix::set_light_bias() {
real_t *m = &matrix[0][0];
diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h
index 2eed6d25d6..60f7d15974 100644
--- a/core/math/camera_matrix.h
+++ b/core/math/camera_matrix.h
@@ -50,6 +50,7 @@ struct CameraMatrix {
void set_identity();
void set_zero();
void set_light_bias();
+ void set_depth_correction(bool p_flip_y = true);
void set_light_atlas_rect(const Rect2 &p_rect);
void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false);
void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov, int p_eye, real_t p_intraocular_dist, real_t p_convergence_dist);
@@ -74,6 +75,7 @@ struct CameraMatrix {
bool get_endpoints(const Transform &p_transform, Vector3 *p_8points) const;
Vector2 get_viewport_half_extents() const;
+ void get_far_plane_size(real_t &r_width, real_t &r_height) const;
void invert();
CameraMatrix inverse() const;
@@ -90,6 +92,23 @@ struct CameraMatrix {
int get_pixels_per_meter(int p_for_pixel_width) const;
operator Transform() const;
+ void flip_y();
+
+ bool operator==(const CameraMatrix &p_cam) const {
+ for (uint32_t i = 0; i < 4; i++) {
+ for (uint32_t j = 0; j < 4; j++) {
+ if (matrix[i][j] != p_cam.matrix[i][j]) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ bool operator!=(const CameraMatrix &p_cam) const {
+ return !(*this == p_cam);
+ }
+
CameraMatrix();
CameraMatrix(const Transform &p_transform);
~CameraMatrix();
diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp
index 71ff79c0fc..353b2acd16 100644
--- a/core/math/vector3.cpp
+++ b/core/math/vector3.cpp
@@ -103,7 +103,7 @@ Vector3 Vector3::cubic_interpolaten(const Vector3 &p_b, const Vector3 &p_pre_a,
Vector3 out;
out = 0.5 * ((p1 * 2.0) +
(-p0 + p2) * t +
- (2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 +
+ (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 +
(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3);
return out;
}
@@ -122,7 +122,7 @@ Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, c
Vector3 out;
out = 0.5 * ((p1 * 2.0) +
(-p0 + p2) * t +
- (2.0 * p0 - 5.0 * p1 + 4 * p2 - p3) * t2 +
+ (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3) * t2 +
(-p0 + 3.0 * p1 - 3.0 * p2 + p3) * t3);
return out;
}
diff --git a/core/math/vector3.h b/core/math/vector3.h
index 4ad3017109..3bf8644af9 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -32,6 +32,7 @@
#define VECTOR3_H
#include "core/math/math_funcs.h"
+#include "core/math/vector3i.h"
#include "core/ustring.h"
class Basis;
@@ -147,6 +148,15 @@ struct Vector3 {
_FORCE_INLINE_ bool operator>=(const Vector3 &p_v) const;
operator String() const;
+ _FORCE_INLINE_ operator Vector3i() const {
+ return Vector3i(x, y, z);
+ }
+
+ _FORCE_INLINE_ Vector3(const Vector3i &p_ivec) {
+ x = p_ivec.x;
+ y = p_ivec.y;
+ z = p_ivec.z;
+ }
_FORCE_INLINE_ Vector3(real_t p_x, real_t p_y, real_t p_z) {
x = p_x;
diff --git a/core/math/vector3i.cpp b/core/math/vector3i.cpp
new file mode 100644
index 0000000000..8a4ddf03b9
--- /dev/null
+++ b/core/math/vector3i.cpp
@@ -0,0 +1,55 @@
+/*************************************************************************/
+/* vector3i.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "vector3i.h"
+
+void Vector3i::set_axis(int p_axis, int32_t p_value) {
+ ERR_FAIL_INDEX(p_axis, 3);
+ coord[p_axis] = p_value;
+}
+int32_t Vector3i::get_axis(int p_axis) const {
+
+ ERR_FAIL_INDEX_V(p_axis, 3, 0);
+ return operator[](p_axis);
+}
+
+int Vector3i::min_axis() const {
+
+ return x < y ? (x < z ? 0 : 2) : (y < z ? 1 : 2);
+}
+int Vector3i::max_axis() const {
+
+ return x < y ? (y < z ? 2 : 1) : (x < z ? 2 : 0);
+}
+
+Vector3i::operator String() const {
+
+ return (itos(x) + ", " + itos(y) + ", " + itos(z));
+}
diff --git a/core/math/vector3i.h b/core/math/vector3i.h
new file mode 100644
index 0000000000..6f9754d3b9
--- /dev/null
+++ b/core/math/vector3i.h
@@ -0,0 +1,272 @@
+/*************************************************************************/
+/* vector3i.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 VECTOR3I_H
+#define VECTOR3I_H
+
+#include "core/typedefs.h"
+#include "core/ustring.h"
+
+struct Vector3i {
+
+ enum Axis {
+ AXIS_X,
+ AXIS_Y,
+ AXIS_Z,
+ };
+
+ union {
+ struct {
+ int32_t x;
+ int32_t y;
+ int32_t z;
+ };
+
+ int32_t coord[3];
+ };
+
+ _FORCE_INLINE_ const int32_t &operator[](int p_axis) const {
+
+ return coord[p_axis];
+ }
+
+ _FORCE_INLINE_ int32_t &operator[](int p_axis) {
+
+ return coord[p_axis];
+ }
+
+ void set_axis(int p_axis, int32_t p_value);
+ int32_t get_axis(int p_axis) const;
+
+ int min_axis() const;
+ int max_axis() const;
+
+ _FORCE_INLINE_ void zero();
+
+ _FORCE_INLINE_ Vector3i abs() const;
+ _FORCE_INLINE_ Vector3i sign() const;
+
+ /* Operators */
+
+ _FORCE_INLINE_ Vector3i &operator+=(const Vector3i &p_v);
+ _FORCE_INLINE_ Vector3i operator+(const Vector3i &p_v) const;
+ _FORCE_INLINE_ Vector3i &operator-=(const Vector3i &p_v);
+ _FORCE_INLINE_ Vector3i operator-(const Vector3i &p_v) const;
+ _FORCE_INLINE_ Vector3i &operator*=(const Vector3i &p_v);
+ _FORCE_INLINE_ Vector3i operator*(const Vector3i &p_v) const;
+ _FORCE_INLINE_ Vector3i &operator/=(const Vector3i &p_v);
+ _FORCE_INLINE_ Vector3i operator/(const Vector3i &p_v) const;
+
+ _FORCE_INLINE_ Vector3i &operator*=(int32_t p_scalar);
+ _FORCE_INLINE_ Vector3i operator*(int32_t p_scalar) const;
+ _FORCE_INLINE_ Vector3i &operator/=(int32_t p_scalar);
+ _FORCE_INLINE_ Vector3i operator/(int32_t p_scalar) const;
+
+ _FORCE_INLINE_ Vector3i operator-() const;
+
+ _FORCE_INLINE_ bool operator==(const Vector3i &p_v) const;
+ _FORCE_INLINE_ bool operator!=(const Vector3i &p_v) const;
+ _FORCE_INLINE_ bool operator<(const Vector3i &p_v) const;
+ _FORCE_INLINE_ bool operator<=(const Vector3i &p_v) const;
+ _FORCE_INLINE_ bool operator>(const Vector3i &p_v) const;
+ _FORCE_INLINE_ bool operator>=(const Vector3i &p_v) const;
+
+ operator String() const;
+
+ _FORCE_INLINE_ Vector3i(int32_t p_x, int32_t p_y, int32_t p_z) {
+ x = p_x;
+ y = p_y;
+ z = p_z;
+ }
+ _FORCE_INLINE_ Vector3i() { x = y = z = 0; }
+};
+
+Vector3i Vector3i::abs() const {
+
+ return Vector3i(ABS(x), ABS(y), ABS(z));
+}
+
+Vector3i Vector3i::sign() const {
+
+ return Vector3i(SGN(x), SGN(y), SGN(z));
+}
+
+/* Operators */
+
+Vector3i &Vector3i::operator+=(const Vector3i &p_v) {
+
+ x += p_v.x;
+ y += p_v.y;
+ z += p_v.z;
+ return *this;
+}
+
+Vector3i Vector3i::operator+(const Vector3i &p_v) const {
+
+ return Vector3i(x + p_v.x, y + p_v.y, z + p_v.z);
+}
+
+Vector3i &Vector3i::operator-=(const Vector3i &p_v) {
+
+ x -= p_v.x;
+ y -= p_v.y;
+ z -= p_v.z;
+ return *this;
+}
+Vector3i Vector3i::operator-(const Vector3i &p_v) const {
+
+ return Vector3i(x - p_v.x, y - p_v.y, z - p_v.z);
+}
+
+Vector3i &Vector3i::operator*=(const Vector3i &p_v) {
+
+ x *= p_v.x;
+ y *= p_v.y;
+ z *= p_v.z;
+ return *this;
+}
+Vector3i Vector3i::operator*(const Vector3i &p_v) const {
+
+ return Vector3i(x * p_v.x, y * p_v.y, z * p_v.z);
+}
+
+Vector3i &Vector3i::operator/=(const Vector3i &p_v) {
+
+ x /= p_v.x;
+ y /= p_v.y;
+ z /= p_v.z;
+ return *this;
+}
+
+Vector3i Vector3i::operator/(const Vector3i &p_v) const {
+
+ return Vector3i(x / p_v.x, y / p_v.y, z / p_v.z);
+}
+
+Vector3i &Vector3i::operator*=(int32_t p_scalar) {
+
+ x *= p_scalar;
+ y *= p_scalar;
+ z *= p_scalar;
+ return *this;
+}
+
+_FORCE_INLINE_ Vector3i operator*(int32_t p_scalar, const Vector3i &p_vec) {
+
+ return p_vec * p_scalar;
+}
+
+Vector3i Vector3i::operator*(int32_t p_scalar) const {
+
+ return Vector3i(x * p_scalar, y * p_scalar, z * p_scalar);
+}
+
+Vector3i &Vector3i::operator/=(int32_t p_scalar) {
+
+ x /= p_scalar;
+ y /= p_scalar;
+ z /= p_scalar;
+ return *this;
+}
+
+Vector3i Vector3i::operator/(int32_t p_scalar) const {
+
+ return Vector3i(x / p_scalar, y / p_scalar, z / p_scalar);
+}
+
+Vector3i Vector3i::operator-() const {
+
+ return Vector3i(-x, -y, -z);
+}
+
+bool Vector3i::operator==(const Vector3i &p_v) const {
+
+ return (x == p_v.x && y == p_v.y && z == p_v.z);
+}
+
+bool Vector3i::operator!=(const Vector3i &p_v) const {
+
+ return (x != p_v.x || y != p_v.y || z != p_v.z);
+}
+
+bool Vector3i::operator<(const Vector3i &p_v) const {
+
+ if (x == p_v.x) {
+ if (y == p_v.y)
+ return z < p_v.z;
+ else
+ return y < p_v.y;
+ } else {
+ return x < p_v.x;
+ }
+}
+
+bool Vector3i::operator>(const Vector3i &p_v) const {
+
+ if (x == p_v.x) {
+ if (y == p_v.y)
+ return z > p_v.z;
+ else
+ return y > p_v.y;
+ } else {
+ return x > p_v.x;
+ }
+}
+
+bool Vector3i::operator<=(const Vector3i &p_v) const {
+
+ if (x == p_v.x) {
+ if (y == p_v.y)
+ return z <= p_v.z;
+ else
+ return y < p_v.y;
+ } else {
+ return x < p_v.x;
+ }
+}
+
+bool Vector3i::operator>=(const Vector3i &p_v) const {
+
+ if (x == p_v.x) {
+ if (y == p_v.y)
+ return z >= p_v.z;
+ else
+ return y > p_v.y;
+ } else {
+ return x > p_v.x;
+ }
+}
+
+void Vector3i::zero() {
+
+ x = y = z = 0;
+}
+
+#endif // VECTOR3I_H
diff --git a/core/oa_hash_map.h b/core/oa_hash_map.h
index 7407c52816..182ed8b116 100644
--- a/core/oa_hash_map.h
+++ b/core/oa_hash_map.h
@@ -240,6 +240,22 @@ public:
return false;
}
+ /**
+ * returns true if the value was found, false otherwise.
+ *
+ * if r_data is not NULL then the value will be written to the object
+ * it points to.
+ */
+ TValue *lookup_ptr(const TKey &p_key) const {
+ uint32_t pos = 0;
+ bool exists = _lookup_pos(p_key, pos);
+
+ if (exists) {
+ return &values[pos];
+ }
+ return NULL;
+ }
+
_FORCE_INLINE_ bool has(const TKey &p_key) const {
uint32_t _pos = 0;
return _lookup_pos(p_key, _pos);
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 7e5c9d6ef8..13be4a5c37 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -691,9 +691,9 @@ const char *OS::get_video_driver_name(int p_driver) const {
switch (p_driver) {
case VIDEO_DRIVER_GLES2:
return "GLES2";
- case VIDEO_DRIVER_GLES3:
+ case VIDEO_DRIVER_VULKAN:
default:
- return "GLES3";
+ return "Vulkan";
}
}
diff --git a/core/os/os.h b/core/os/os.h
index 89b3414b3e..1bb910cb0d 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -181,7 +181,7 @@ public:
virtual void get_fullscreen_mode_list(List<VideoMode> *p_list, int p_screen = 0) const = 0;
enum VideoDriver {
- VIDEO_DRIVER_GLES3,
+ VIDEO_DRIVER_VULKAN,
VIDEO_DRIVER_GLES2,
VIDEO_DRIVER_MAX,
};
diff --git a/core/os/semaphore.cpp b/core/os/semaphore.cpp
index b2ba9716f0..2c20f234d0 100644
--- a/core/os/semaphore.cpp
+++ b/core/os/semaphore.cpp
@@ -32,14 +32,14 @@
#include "core/error_macros.h"
-Semaphore *(*Semaphore::create_func)() = 0;
+SemaphoreOld *(*SemaphoreOld::create_func)() = 0;
-Semaphore *Semaphore::create() {
+SemaphoreOld *SemaphoreOld::create() {
ERR_FAIL_COND_V(!create_func, 0);
return create_func();
}
-Semaphore::~Semaphore() {
+SemaphoreOld::~SemaphoreOld() {
}
diff --git a/core/os/semaphore.h b/core/os/semaphore.h
index 9f3c0f549c..f16a15a6db 100644
--- a/core/os/semaphore.h
+++ b/core/os/semaphore.h
@@ -32,19 +32,53 @@
#define SEMAPHORE_H
#include "core/error_list.h"
+#include "core/typedefs.h"
+
+#include <condition_variable>
+#include <mutex>
class Semaphore {
+private:
+ std::mutex mutex_;
+ std::condition_variable condition_;
+ unsigned long count_ = 0; // Initialized as locked.
+
+public:
+ _ALWAYS_INLINE_ void post() {
+ std::lock_guard<decltype(mutex_)> lock(mutex_);
+ ++count_;
+ condition_.notify_one();
+ }
+
+ _ALWAYS_INLINE_ void wait() {
+ std::unique_lock<decltype(mutex_)> lock(mutex_);
+ while (!count_) // Handle spurious wake-ups.
+ condition_.wait(lock);
+ --count_;
+ }
+
+ _ALWAYS_INLINE_ bool try_wait() {
+ std::lock_guard<decltype(mutex_)> lock(mutex_);
+ if (count_) {
+ --count_;
+ return true;
+ }
+ return false;
+ }
+};
+
+class SemaphoreOld {
protected:
- static Semaphore *(*create_func)();
+ static SemaphoreOld *(*create_func)();
public:
virtual Error wait() = 0; ///< wait until semaphore has positive value, then decrement and pass
virtual Error post() = 0; ///< unlock the semaphore, incrementing the value
virtual int get() const = 0; ///< get semaphore value
- static Semaphore *create(); ///< Create a mutex
+ static SemaphoreOld *create(); ///< Create a mutex
- virtual ~Semaphore();
+ virtual ~SemaphoreOld();
};
#endif
diff --git a/core/os/thread_dummy.cpp b/core/os/thread_dummy.cpp
index d4f65b0312..916aeeda30 100644
--- a/core/os/thread_dummy.cpp
+++ b/core/os/thread_dummy.cpp
@@ -48,12 +48,12 @@ void MutexDummy::make_default() {
Mutex::create_func = &MutexDummy::create;
};
-Semaphore *SemaphoreDummy::create() {
+SemaphoreOld *SemaphoreDummy::create() {
return memnew(SemaphoreDummy);
};
void SemaphoreDummy::make_default() {
- Semaphore::create_func = &SemaphoreDummy::create;
+ SemaphoreOld::create_func = &SemaphoreDummy::create;
};
RWLock *RWLockDummy::create() {
diff --git a/core/os/thread_dummy.h b/core/os/thread_dummy.h
index c8b52ae4dd..9329cdaa32 100644
--- a/core/os/thread_dummy.h
+++ b/core/os/thread_dummy.h
@@ -58,9 +58,9 @@ public:
static void make_default();
};
-class SemaphoreDummy : public Semaphore {
+class SemaphoreDummy : public SemaphoreOld {
- static Semaphore *create();
+ static SemaphoreOld *create();
public:
virtual Error wait() { return OK; };
diff --git a/core/rid.h b/core/rid.h
index 7e12409181..0c4a96efed 100644
--- a/core/rid.h
+++ b/core/rid.h
@@ -32,172 +32,46 @@
#define RID_H
#include "core/list.h"
+#include "core/oa_hash_map.h"
#include "core/os/memory.h"
#include "core/safe_refcount.h"
#include "core/set.h"
#include "core/typedefs.h"
-class RID_OwnerBase;
-
-class RID_Data {
-
- friend class RID_OwnerBase;
-
-#ifndef DEBUG_ENABLED
- RID_OwnerBase *_owner;
-#endif
- uint32_t _id;
-
-public:
- _FORCE_INLINE_ uint32_t get_id() const { return _id; }
-
- virtual ~RID_Data();
-};
+class RID_AllocBase;
class RID {
- friend class RID_OwnerBase;
-
- mutable RID_Data *_data;
+ friend class RID_AllocBase;
+ uint64_t _id;
public:
- _FORCE_INLINE_ RID_Data *get_data() const { return _data; }
-
_FORCE_INLINE_ bool operator==(const RID &p_rid) const {
- return _data == p_rid._data;
+ return _id == p_rid._id;
}
_FORCE_INLINE_ bool operator<(const RID &p_rid) const {
- return _data < p_rid._data;
+ return _id < p_rid._id;
}
_FORCE_INLINE_ bool operator<=(const RID &p_rid) const {
- return _data <= p_rid._data;
+ return _id <= p_rid._id;
}
_FORCE_INLINE_ bool operator>(const RID &p_rid) const {
- return _data > p_rid._data;
+ return _id > p_rid._id;
}
_FORCE_INLINE_ bool operator!=(const RID &p_rid) const {
- return _data != p_rid._data;
+ return _id != p_rid._id;
}
- _FORCE_INLINE_ bool is_valid() const { return _data != NULL; }
+ _FORCE_INLINE_ bool is_valid() const { return _id != 0; }
+ _FORCE_INLINE_ bool is_null() const { return _id == 0; }
- _FORCE_INLINE_ uint32_t get_id() const { return _data ? _data->get_id() : 0; }
+ _FORCE_INLINE_ uint64_t get_id() const { return _id; }
_FORCE_INLINE_ RID() {
- _data = NULL;
- }
-};
-
-class RID_OwnerBase {
-protected:
- static SafeRefCount refcount;
- _FORCE_INLINE_ void _set_data(RID &p_rid, RID_Data *p_data) {
- p_rid._data = p_data;
- refcount.ref();
- p_data->_id = refcount.get();
-#ifndef DEBUG_ENABLED
- p_data->_owner = this;
-#endif
- }
-
-#ifndef DEBUG_ENABLED
-
- _FORCE_INLINE_ bool _is_owner(const RID &p_rid) const {
-
- return this == p_rid._data->_owner;
- }
-
- _FORCE_INLINE_ void _remove_owner(RID &p_rid) {
-
- p_rid._data->_owner = NULL;
- }
-#endif
-
-public:
- virtual void get_owned_list(List<RID> *p_owned) = 0;
-
- static void init_rid();
- virtual ~RID_OwnerBase() {}
-};
-
-template <class T>
-class RID_Owner : public RID_OwnerBase {
-public:
-#ifdef DEBUG_ENABLED
- mutable Set<RID_Data *> id_map;
-#endif
-public:
- _FORCE_INLINE_ RID make_rid(T *p_data) {
-
- RID rid;
- _set_data(rid, p_data);
-
-#ifdef DEBUG_ENABLED
- id_map.insert(p_data);
-#endif
-
- return rid;
- }
-
- _FORCE_INLINE_ T *get(const RID &p_rid) {
-
-#ifdef DEBUG_ENABLED
-
- ERR_FAIL_COND_V(!p_rid.is_valid(), NULL);
- ERR_FAIL_COND_V(!id_map.has(p_rid.get_data()), NULL);
-#endif
- return static_cast<T *>(p_rid.get_data());
- }
-
- _FORCE_INLINE_ T *getornull(const RID &p_rid) {
-
-#ifdef DEBUG_ENABLED
-
- if (p_rid.get_data()) {
- ERR_FAIL_COND_V(!id_map.has(p_rid.get_data()), NULL);
- }
-#endif
- return static_cast<T *>(p_rid.get_data());
- }
-
- _FORCE_INLINE_ T *getptr(const RID &p_rid) {
-
- return static_cast<T *>(p_rid.get_data());
- }
-
- _FORCE_INLINE_ bool owns(const RID &p_rid) const {
-
- if (p_rid.get_data() == NULL)
- return false;
-#ifdef DEBUG_ENABLED
- return id_map.has(p_rid.get_data());
-#else
- return _is_owner(p_rid);
-#endif
- }
-
- void free(RID p_rid) {
-
-#ifdef DEBUG_ENABLED
- id_map.erase(p_rid.get_data());
-#else
- _remove_owner(p_rid);
-#endif
- }
-
- void get_owned_list(List<RID> *p_owned) {
-
-#ifdef DEBUG_ENABLED
-
- for (typename Set<RID_Data *>::Element *E = id_map.front(); E; E = E->next()) {
- RID r;
- _set_data(r, static_cast<T *>(E->get()));
- p_owned->push_back(r);
- }
-#endif
+ _id = 0;
}
};
diff --git a/core/rid.cpp b/core/rid_owner.cpp
index 727658314f..a5065f29f8 100644
--- a/core/rid.cpp
+++ b/core/rid_owner.cpp
@@ -1,5 +1,5 @@
/*************************************************************************/
-/* rid.cpp */
+/* rid_owner.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
@@ -28,14 +28,6 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "rid.h"
+#include "rid_owner.h"
-RID_Data::~RID_Data() {
-}
-
-SafeRefCount RID_OwnerBase::refcount;
-
-void RID_OwnerBase::init_rid() {
-
- refcount.init();
-}
+volatile uint64_t RID_AllocBase::base_id = 1;
diff --git a/core/rid_owner.h b/core/rid_owner.h
new file mode 100644
index 0000000000..bd01eba17d
--- /dev/null
+++ b/core/rid_owner.h
@@ -0,0 +1,406 @@
+/*************************************************************************/
+/* rid_owner.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 RID_OWNER_H
+#define RID_OWNER_H
+
+#include "core/print_string.h"
+#include "core/rid.h"
+#include "core/spin_lock.h"
+#include <stdio.h>
+#include <typeinfo>
+
+class RID_AllocBase {
+
+ static volatile uint64_t base_id;
+
+protected:
+ static RID _make_from_id(uint64_t p_id) {
+ RID rid;
+ rid._id = p_id;
+ return rid;
+ }
+
+ static uint64_t _gen_id() {
+ return atomic_increment(&base_id);
+ }
+
+ static RID _gen_rid() {
+ return _make_from_id(_gen_id());
+ }
+
+public:
+ virtual ~RID_AllocBase() {}
+};
+
+template <class T, bool THREAD_SAFE = false>
+class RID_Alloc : public RID_AllocBase {
+
+ T **chunks;
+ uint32_t **free_list_chunks;
+ uint32_t **validator_chunks;
+
+ uint32_t elements_in_chunk;
+ uint32_t max_alloc;
+ uint32_t alloc_count;
+
+ const char *description;
+
+ SpinLock spin_lock;
+
+public:
+ RID make_rid(const T &p_value) {
+
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+
+ if (alloc_count == max_alloc) {
+ //allocate a new chunk
+ uint32_t chunk_count = alloc_count == 0 ? 0 : (max_alloc / elements_in_chunk);
+
+ //grow chunks
+ chunks = (T **)memrealloc(chunks, sizeof(T *) * (chunk_count + 1));
+ chunks[chunk_count] = (T *)memalloc(sizeof(T) * elements_in_chunk); //but don't initialize
+
+ //grow validators
+ validator_chunks = (uint32_t **)memrealloc(validator_chunks, sizeof(uint32_t *) * (chunk_count + 1));
+ validator_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk);
+ //grow free lists
+ free_list_chunks = (uint32_t **)memrealloc(free_list_chunks, sizeof(uint32_t *) * (chunk_count + 1));
+ free_list_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk);
+
+ //initialize
+ for (uint32_t i = 0; i < elements_in_chunk; i++) {
+ //dont initialize chunk
+ validator_chunks[chunk_count][i] = 0xFFFFFFFF;
+ free_list_chunks[chunk_count][i] = alloc_count + i;
+ }
+
+ max_alloc += elements_in_chunk;
+ }
+
+ uint32_t free_index = free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk];
+
+ uint32_t free_chunk = free_index / elements_in_chunk;
+ uint32_t free_element = free_index % elements_in_chunk;
+
+ T *ptr = &chunks[free_chunk][free_element];
+ memnew_placement(ptr, T(p_value));
+
+ uint32_t validator = (uint32_t)(_gen_id() & 0xFFFFFFFF);
+ uint64_t id = validator;
+ id <<= 32;
+ id |= free_index;
+
+ validator_chunks[free_chunk][free_element] = validator;
+ alloc_count++;
+
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+
+ return _make_from_id(id);
+ }
+
+ _FORCE_INLINE_ T *getornull(const RID &p_rid) {
+
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+
+ uint64_t id = p_rid.get_id();
+ uint32_t idx = uint32_t(id & 0xFFFFFFFF);
+ if (unlikely(idx >= max_alloc)) {
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ return NULL;
+ }
+
+ uint32_t idx_chunk = idx / elements_in_chunk;
+ uint32_t idx_element = idx % elements_in_chunk;
+
+ uint32_t validator = uint32_t(id >> 32);
+ if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ return NULL;
+ }
+
+ T *ptr = &chunks[idx_chunk][idx_element];
+
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+
+ return ptr;
+ }
+
+ _FORCE_INLINE_ bool owns(const RID &p_rid) {
+
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+
+ uint64_t id = p_rid.get_id();
+ uint32_t idx = uint32_t(id & 0xFFFFFFFF);
+ if (unlikely(idx >= max_alloc)) {
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ return false;
+ }
+
+ uint32_t idx_chunk = idx / elements_in_chunk;
+ uint32_t idx_element = idx % elements_in_chunk;
+
+ uint32_t validator = uint32_t(id >> 32);
+
+ bool owned = validator_chunks[idx_chunk][idx_element] == validator;
+
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+
+ return owned;
+ }
+
+ _FORCE_INLINE_ void free(const RID &p_rid) {
+
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+
+ uint64_t id = p_rid.get_id();
+ uint32_t idx = uint32_t(id & 0xFFFFFFFF);
+ if (unlikely(idx >= max_alloc)) {
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ ERR_FAIL();
+ }
+
+ uint32_t idx_chunk = idx / elements_in_chunk;
+ uint32_t idx_element = idx % elements_in_chunk;
+
+ uint32_t validator = uint32_t(id >> 32);
+ if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ ERR_FAIL();
+ }
+
+ chunks[idx_chunk][idx_element].~T();
+ validator_chunks[idx_chunk][idx_element] = 0xFFFFFFFF; // go invalid
+
+ alloc_count--;
+ free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk] = idx;
+
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ }
+
+ _FORCE_INLINE_ uint32_t get_rid_count() const {
+ return alloc_count;
+ }
+
+ _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) {
+ ERR_FAIL_INDEX_V(p_index, alloc_count, NULL);
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+ uint64_t idx = free_list_chunks[p_index / elements_in_chunk][p_index % elements_in_chunk];
+ T *ptr = &chunks[idx / elements_in_chunk][idx % elements_in_chunk];
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ return ptr;
+ }
+
+ _FORCE_INLINE_ RID get_rid_by_index(uint32_t p_index) {
+ ERR_FAIL_INDEX_V(p_index, alloc_count, RID());
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+ uint64_t idx = free_list_chunks[p_index / elements_in_chunk][p_index % elements_in_chunk];
+ uint64_t validator = validator_chunks[idx / elements_in_chunk][idx % elements_in_chunk];
+
+ RID rid = _make_from_id((validator << 32) | idx);
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ return rid;
+ }
+
+ void get_owned_list(List<RID> *p_owned) {
+ if (THREAD_SAFE) {
+ spin_lock.lock();
+ }
+ for (size_t i = 0; i < max_alloc; i++) {
+ uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
+ if (validator != 0xFFFFFFFF) {
+ p_owned->push_back(_make_from_id((validator << 32) | i));
+ }
+ }
+ if (THREAD_SAFE) {
+ spin_lock.unlock();
+ }
+ }
+
+ void set_description(const char *p_descrption) {
+ description = p_descrption;
+ }
+
+ RID_Alloc(uint32_t p_target_chunk_byte_size = 4096) {
+ chunks = NULL;
+ free_list_chunks = NULL;
+ validator_chunks = NULL;
+
+ elements_in_chunk = sizeof(T) > p_target_chunk_byte_size ? 1 : (p_target_chunk_byte_size / sizeof(T));
+ max_alloc = 0;
+ alloc_count = 0;
+ description = NULL;
+ }
+
+ ~RID_Alloc() {
+ if (alloc_count) {
+ if (description) {
+ print_error("ERROR: " + itos(alloc_count) + " RID allocations of type '" + description + "' were leaked at exit.");
+ } else {
+ print_error("ERROR: " + itos(alloc_count) + " RID allocations of type '" + typeid(T).name() + "' were leaked at exit.");
+ }
+
+ for (size_t i = 0; i < max_alloc; i++) {
+ uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
+ if (validator != 0xFFFFFFFF) {
+ chunks[i / elements_in_chunk][i % elements_in_chunk].~T();
+ }
+ }
+ }
+
+ uint32_t chunk_count = max_alloc / elements_in_chunk;
+ for (uint32_t i = 0; i < chunk_count; i++) {
+ memfree(chunks[i]);
+ memfree(validator_chunks[i]);
+ memfree(free_list_chunks[i]);
+ }
+
+ if (chunks) {
+ memfree(chunks);
+ memfree(free_list_chunks);
+ memfree(validator_chunks);
+ }
+ }
+};
+
+template <class T, bool THREAD_SAFE = false>
+class RID_PtrOwner {
+ RID_Alloc<T *, THREAD_SAFE> alloc;
+
+public:
+ _FORCE_INLINE_ RID make_rid(T *p_ptr) {
+ return alloc.make_rid(p_ptr);
+ }
+
+ _FORCE_INLINE_ T *getornull(const RID &p_rid) {
+ T **ptr = alloc.getornull(p_rid);
+ if (unlikely(!ptr)) {
+ return NULL;
+ }
+ return *ptr;
+ }
+
+ _FORCE_INLINE_ bool owns(const RID &p_rid) {
+ return alloc.owns(p_rid);
+ }
+
+ _FORCE_INLINE_ void free(const RID &p_rid) {
+ alloc.free(p_rid);
+ }
+
+ _FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) {
+ return alloc.get_owned_list(p_owned);
+ }
+
+ void set_description(const char *p_descrption) {
+ alloc.set_description(p_descrption);
+ }
+ RID_PtrOwner(uint32_t p_target_chunk_byte_size = 4096) :
+ alloc(p_target_chunk_byte_size) {}
+};
+
+template <class T, bool THREAD_SAFE = false>
+class RID_Owner {
+ RID_Alloc<T, THREAD_SAFE> alloc;
+
+public:
+ _FORCE_INLINE_ RID make_rid(const T &p_ptr) {
+ return alloc.make_rid(p_ptr);
+ }
+
+ _FORCE_INLINE_ T *getornull(const RID &p_rid) {
+ return alloc.getornull(p_rid);
+ }
+
+ _FORCE_INLINE_ bool owns(const RID &p_rid) {
+ return alloc.owns(p_rid);
+ }
+
+ _FORCE_INLINE_ void free(const RID &p_rid) {
+ alloc.free(p_rid);
+ }
+
+ _FORCE_INLINE_ uint32_t get_rid_count() const {
+ return alloc.get_rid_count();
+ }
+
+ _FORCE_INLINE_ RID get_rid_by_index(uint32_t p_index) {
+ return alloc.get_rid_by_index(p_index);
+ }
+
+ _FORCE_INLINE_ T *get_ptr_by_index(uint32_t p_index) {
+ return alloc.get_ptr_by_index(p_index);
+ }
+
+ _FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) {
+ return alloc.get_owned_list(p_owned);
+ }
+
+ void set_description(const char *p_descrption) {
+ alloc.set_description(p_descrption);
+ }
+ RID_Owner(uint32_t p_target_chunk_byte_size = 4096) :
+ alloc(p_target_chunk_byte_size) {}
+};
+#endif // RID_OWNER_H
diff --git a/core/spin_lock.h b/core/spin_lock.h
new file mode 100644
index 0000000000..c48631f94a
--- /dev/null
+++ b/core/spin_lock.h
@@ -0,0 +1,50 @@
+/*************************************************************************/
+/* spin_lock.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 SPIN_LOCK_H
+#define SPIN_LOCK_H
+
+#include "core/typedefs.h"
+#include <atomic>
+
+class SpinLock {
+ std::atomic_flag locked = ATOMIC_FLAG_INIT;
+
+public:
+ _ALWAYS_INLINE_ void lock() {
+ while (locked.test_and_set(std::memory_order_acquire)) {
+ ;
+ }
+ }
+ _ALWAYS_INLINE_ void unlock() {
+ locked.clear(std::memory_order_release);
+ }
+};
+#endif // SPIN_LOCK_H
diff --git a/core/thread_work_pool.cpp b/core/thread_work_pool.cpp
new file mode 100644
index 0000000000..c8311f102f
--- /dev/null
+++ b/core/thread_work_pool.cpp
@@ -0,0 +1,83 @@
+/*************************************************************************/
+/* thread_work_pool.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 "thread_work_pool.h"
+#include "core/os/os.h"
+
+void ThreadWorkPool::_thread_function(ThreadData *p_thread) {
+
+ while (true) {
+ p_thread->start.wait();
+ if (p_thread->exit.load()) {
+ break;
+ }
+ p_thread->work->work();
+ p_thread->completed.post();
+ }
+}
+
+void ThreadWorkPool::init(int p_thread_count) {
+ ERR_FAIL_COND(threads != nullptr);
+ if (p_thread_count < 0) {
+ p_thread_count = OS::get_singleton()->get_processor_count();
+ }
+
+ thread_count = p_thread_count;
+ threads = memnew_arr(ThreadData, thread_count);
+
+ for (uint32_t i = 0; i < thread_count; i++) {
+ threads[i].exit.store(false);
+ threads[i].thread = memnew(std::thread(ThreadWorkPool::_thread_function, &threads[i]));
+ }
+}
+
+void ThreadWorkPool::finish() {
+
+ if (threads == nullptr) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < thread_count; i++) {
+ threads[i].exit.store(true);
+ threads[i].start.post();
+ }
+ for (uint32_t i = 0; i < thread_count; i++) {
+ threads[i].thread->join();
+ memdelete(threads[i].thread);
+ }
+
+ memdelete_arr(threads);
+ threads = nullptr;
+}
+
+ThreadWorkPool::~ThreadWorkPool() {
+
+ finish();
+}
diff --git a/core/thread_work_pool.h b/core/thread_work_pool.h
new file mode 100644
index 0000000000..3dedb40752
--- /dev/null
+++ b/core/thread_work_pool.h
@@ -0,0 +1,108 @@
+/*************************************************************************/
+/* thread_work_pool.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 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 THREAD_WORK_POOL_H
+#define THREAD_WORK_POOL_H
+
+#include "core/os/memory.h"
+#include "core/os/semaphore.h"
+#include <atomic>
+#include <thread>
+class ThreadWorkPool {
+
+ std::atomic<uint32_t> index;
+
+ struct BaseWork {
+ std::atomic<uint32_t> *index;
+ uint32_t max_elements;
+ virtual void work() = 0;
+ };
+
+ template <class C, class M, class U>
+ struct Work : public BaseWork {
+ C *instance;
+ M method;
+ U userdata;
+ virtual void work() {
+
+ while (true) {
+ uint32_t work_index = index->fetch_add(1, std::memory_order_relaxed);
+ if (work_index >= max_elements) {
+ break;
+ }
+ (instance->*method)(work_index, userdata);
+ }
+ }
+ };
+
+ struct ThreadData {
+ std::thread *thread;
+ Semaphore start;
+ Semaphore completed;
+ std::atomic<bool> exit;
+ BaseWork *work;
+ };
+
+ ThreadData *threads = nullptr;
+ uint32_t thread_count = 0;
+
+ static void _thread_function(ThreadData *p_thread);
+
+public:
+ template <class C, class M, class U>
+ void do_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) {
+
+ ERR_FAIL_COND(!threads); //never initialized
+
+ index.store(0);
+
+ Work<C, M, U> *w = memnew((Work<C, M, U>));
+ w->instance = p_instance;
+ w->userdata = p_userdata;
+ w->method = p_method;
+ w->index = &index;
+ w->max_elements = p_elements;
+
+ for (uint32_t i = 0; i < thread_count; i++) {
+ threads[i].work = w;
+ threads[i].start.post();
+ }
+ for (uint32_t i = 0; i < thread_count; i++) {
+ threads[i].completed.wait();
+ threads[i].work = nullptr;
+ }
+ }
+
+ void init(int p_thread_count = -1);
+ void finish();
+ ~ThreadWorkPool();
+};
+
+#endif // THREAD_POOL_H
diff --git a/core/ustring.cpp b/core/ustring.cpp
index 8030efcc2b..c4543b89da 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -646,6 +646,17 @@ String String::camelcase_to_underscore(bool lowercase) const {
return lowercase ? new_string.to_lower() : new_string;
}
+String String::get_with_code_lines() const {
+ Vector<String> lines = split("\n");
+ String ret;
+ for (int i = 0; i < lines.size(); i++) {
+ if (i > 0) {
+ ret += "\n";
+ }
+ ret += itos(i + 1) + " " + lines[i];
+ }
+ return ret;
+}
int String::get_slice_count(String p_splitter) const {
if (empty())
diff --git a/core/ustring.h b/core/ustring.h
index 5bf73001aa..e70b2bfe27 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -258,6 +258,7 @@ public:
String capitalize() const;
String camelcase_to_underscore(bool lowercase = true) const;
+ String get_with_code_lines() const;
int get_slice_count(String p_splitter) const;
String get_slice(String p_splitter, int p_slice) const;
String get_slicec(CharType p_splitter, int p_slice) const;