summaryrefslogtreecommitdiffstats
path: root/drivers/gles3/storage/texture_storage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gles3/storage/texture_storage.cpp')
-rw-r--r--drivers/gles3/storage/texture_storage.cpp1153
1 files changed, 921 insertions, 232 deletions
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index 543638e8ff..15743c2d78 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -34,6 +34,10 @@
#include "config.h"
#include "drivers/gles3/effects/copy_effects.h"
+#ifdef ANDROID_ENABLED
+#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR
+#endif
+
using namespace GLES3;
TextureStorage *TextureStorage::singleton = nullptr;
@@ -59,9 +63,7 @@ TextureStorage::TextureStorage() {
{ //create default textures
{ // White Textures
- Ref<Image> image;
- image.instantiate();
- image->create(4, 4, true, Image::FORMAT_RGBA8);
+ Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8);
image->fill(Color(1, 1, 1, 1));
image->generate_mipmaps();
@@ -90,9 +92,7 @@ TextureStorage::TextureStorage() {
}
{ // black
- Ref<Image> image;
- image.instantiate();
- image->create(4, 4, true, Image::FORMAT_RGBA8);
+ Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8);
image->fill(Color(0, 0, 0, 1));
image->generate_mipmaps();
@@ -116,9 +116,7 @@ TextureStorage::TextureStorage() {
}
{ // transparent black
- Ref<Image> image;
- image.instantiate();
- image->create(4, 4, true, Image::FORMAT_RGBA8);
+ Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8);
image->fill(Color(0, 0, 0, 0));
image->generate_mipmaps();
@@ -127,9 +125,7 @@ TextureStorage::TextureStorage() {
}
{
- Ref<Image> image;
- image.instantiate();
- image->create(4, 4, true, Image::FORMAT_RGBA8);
+ Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8);
image->fill(Color(0.5, 0.5, 1, 1));
image->generate_mipmaps();
@@ -138,9 +134,7 @@ TextureStorage::TextureStorage() {
}
{
- Ref<Image> image;
- image.instantiate();
- image->create(4, 4, true, Image::FORMAT_RGBA8);
+ Ref<Image> image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8);
image->fill(Color(1.0, 0.5, 1, 1));
image->generate_mipmaps();
@@ -197,6 +191,27 @@ TextureStorage::TextureStorage() {
glBindTexture(GL_TEXTURE_2D, 0);
+ { // Atlas Texture initialize.
+ uint8_t pixel_data[4 * 4 * 4];
+ for (int i = 0; i < 16; i++) {
+ pixel_data[i * 4 + 0] = 0;
+ pixel_data[i * 4 + 1] = 0;
+ pixel_data[i * 4 + 2] = 0;
+ pixel_data[i * 4 + 3] = 255;
+ }
+
+ glGenTextures(1, &texture_atlas.texture);
+ glBindTexture(GL_TEXTURE_2D, texture_atlas.texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixel_data);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ {
+ sdf_shader.shader.initialize();
+ sdf_shader.shader_version = sdf_shader.shader.version_create();
+ }
+
#ifdef GLES_OVER_GL
glEnable(GL_PROGRAM_POINT_SIZE);
#endif
@@ -207,6 +222,12 @@ TextureStorage::~TextureStorage() {
for (int i = 0; i < DEFAULT_GL_TEXTURE_MAX; i++) {
texture_free(default_gl_textures[i]);
}
+
+ glDeleteTextures(1, &texture_atlas.texture);
+ texture_atlas.texture = 0;
+ glDeleteFramebuffers(1, &texture_atlas.framebuffer);
+ texture_atlas.framebuffer = 0;
+ sdf_shader.shader.version_free(sdf_shader.shader_version);
}
//TODO, move back to storage
@@ -230,6 +251,8 @@ void TextureStorage::canvas_texture_free(RID p_rid) {
void TextureStorage::canvas_texture_set_channel(RID p_canvas_texture, RS::CanvasTextureChannel p_channel, RID p_texture) {
CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture);
+ ERR_FAIL_NULL(ct);
+
switch (p_channel) {
case RS::CANVAS_TEXTURE_CHANNEL_DIFFUSE: {
ct->diffuse = p_texture;
@@ -245,6 +268,8 @@ void TextureStorage::canvas_texture_set_channel(RID p_canvas_texture, RS::Canvas
void TextureStorage::canvas_texture_set_shading_parameters(RID p_canvas_texture, const Color &p_specular_color, float p_shininess) {
CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture);
+ ERR_FAIL_NULL(ct);
+
ct->specular_color.r = p_specular_color.r;
ct->specular_color.g = p_specular_color.g;
ct->specular_color.b = p_specular_color.b;
@@ -253,61 +278,16 @@ void TextureStorage::canvas_texture_set_shading_parameters(RID p_canvas_texture,
void TextureStorage::canvas_texture_set_texture_filter(RID p_canvas_texture, RS::CanvasItemTextureFilter p_filter) {
CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture);
+ ERR_FAIL_NULL(ct);
+
ct->texture_filter = p_filter;
}
void TextureStorage::canvas_texture_set_texture_repeat(RID p_canvas_texture, RS::CanvasItemTextureRepeat p_repeat) {
CanvasTexture *ct = canvas_texture_owner.get_or_null(p_canvas_texture);
- ct->texture_repeat = p_repeat;
-}
-
-/* CANVAS SHADOW */
-
-RID TextureStorage::canvas_light_shadow_buffer_create(int p_width) {
- Config *config = Config::get_singleton();
- CanvasLightShadow *cls = memnew(CanvasLightShadow);
-
- if (p_width > config->max_texture_size) {
- p_width = config->max_texture_size;
- }
-
- cls->size = p_width;
- cls->height = 16;
-
- glActiveTexture(GL_TEXTURE0);
-
- glGenFramebuffers(1, &cls->fbo);
- glBindFramebuffer(GL_FRAMEBUFFER, cls->fbo);
+ ERR_FAIL_NULL(ct);
- glGenRenderbuffers(1, &cls->depth);
- glBindRenderbuffer(GL_RENDERBUFFER, cls->depth);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, cls->size, cls->height);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, cls->depth);
-
- glGenTextures(1, &cls->distance);
- glBindTexture(GL_TEXTURE_2D, cls->distance);
- if (config->use_rgba_2d_shadows) {
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, cls->size, cls->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
- } else {
- glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, cls->size, cls->height, 0, GL_RED, GL_FLOAT, nullptr);
- }
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, cls->distance, 0);
-
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- //printf("errnum: %x\n",status);
- glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
-
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- memdelete(cls);
- ERR_FAIL_COND_V(status != GL_FRAMEBUFFER_COMPLETE, RID());
- }
-
- return canvas_light_shadow_owner.make_rid(cls);
+ ct->texture_repeat = p_repeat;
}
/* Texture API */
@@ -642,7 +622,9 @@ void TextureStorage::texture_free(RID p_texture) {
}
if (t->tex_id != 0) {
- glDeleteTextures(1, &t->tex_id);
+ if (!t->is_external) {
+ glDeleteTextures(1, &t->tex_id);
+ }
t->tex_id = 0;
}
@@ -653,7 +635,7 @@ void TextureStorage::texture_free(RID p_texture) {
}
}
- //decal_atlas_remove_texture(p_texture);
+ texture_atlas_remove_texture(p_texture);
for (int i = 0; i < t->proxies.size(); i++) {
Texture *p = texture_owner.get_or_null(t->proxies[i]);
@@ -666,12 +648,14 @@ void TextureStorage::texture_free(RID p_texture) {
}
void TextureStorage::texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) {
+ ERR_FAIL_COND(p_image.is_null());
+
Texture texture;
texture.width = p_image->get_width();
texture.height = p_image->get_height();
texture.alloc_width = texture.width;
texture.alloc_height = texture.height;
- texture.mipmaps = p_image->get_mipmap_count();
+ texture.mipmaps = p_image->get_mipmap_count() + 1;
texture.format = p_image->get_format();
texture.type = Texture::TYPE_2D;
texture.target = GL_TEXTURE_2D;
@@ -706,9 +690,37 @@ void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) {
texture_owner.initialize_rid(p_texture, proxy_tex);
}
+RID TextureStorage::texture_create_external(Texture::Type p_type, Image::Format p_format, unsigned int p_image, int p_width, int p_height, int p_depth, int p_layers, RS::TextureLayeredType p_layered_type) {
+ Texture texture;
+ texture.active = true;
+ texture.is_external = true;
+ texture.type = p_type;
+
+ switch (p_type) {
+ case Texture::TYPE_2D: {
+ texture.target = GL_TEXTURE_2D;
+ } break;
+ case Texture::TYPE_3D: {
+ texture.target = GL_TEXTURE_3D;
+ } break;
+ case Texture::TYPE_LAYERED: {
+ texture.target = GL_TEXTURE_2D_ARRAY;
+ } break;
+ }
+
+ texture.real_format = texture.format = p_format;
+ texture.tex_id = p_image;
+ texture.alloc_width = texture.width = p_width;
+ texture.alloc_height = texture.height = p_height;
+ texture.depth = p_depth;
+ texture.layers = p_layers;
+ texture.layered_type = p_layered_type;
+
+ return texture_owner.make_rid(texture);
+}
+
void TextureStorage::texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer) {
- // only 1 layer so far
- texture_set_data(p_texture, p_image);
+ texture_set_data(p_texture, p_image, p_layer);
#ifdef TOOLS_ENABLED
Texture *tex = texture_owner.get_or_null(p_texture);
@@ -717,14 +729,32 @@ void TextureStorage::texture_2d_update(RID p_texture, const Ref<Image> &p_image,
}
void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) {
+ Texture *tex = texture_owner.get_or_null(p_texture);
+ ERR_FAIL_COND(!tex);
+ ERR_FAIL_COND(!tex->is_proxy);
+ Texture *proxy_to = texture_owner.get_or_null(p_proxy_to);
+ ERR_FAIL_COND(!proxy_to);
+ ERR_FAIL_COND(proxy_to->is_proxy);
+
+ if (tex->proxy_to.is_valid()) {
+ Texture *prev_tex = texture_owner.get_or_null(tex->proxy_to);
+ ERR_FAIL_COND(!prev_tex);
+ prev_tex->proxies.erase(p_texture);
+ }
+
+ *tex = *proxy_to;
+
+ tex->proxy_to = p_proxy_to;
+ tex->is_render_target = false;
+ tex->is_proxy = true;
+ tex->proxies.clear();
+ proxy_to->proxies.push_back(p_texture);
}
void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) {
//this could be better optimized to reuse an existing image , done this way
//for now to get it working
- Ref<Image> image;
- image.instantiate();
- image->create(4, 4, false, Image::FORMAT_RGBA8);
+ Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(1, 0, 1, 1));
texture_2d_initialize(p_texture, image);
@@ -733,9 +763,7 @@ void TextureStorage::texture_2d_placeholder_initialize(RID p_texture) {
void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, RenderingServer::TextureLayeredType p_layered_type) {
//this could be better optimized to reuse an existing image , done this way
//for now to get it working
- Ref<Image> image;
- image.instantiate();
- image->create(4, 4, false, Image::FORMAT_RGBA8);
+ Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(1, 0, 1, 1));
Vector<Ref<Image>> images;
@@ -754,9 +782,7 @@ void TextureStorage::texture_2d_layered_placeholder_initialize(RID p_texture, Re
void TextureStorage::texture_3d_placeholder_initialize(RID p_texture) {
//this could be better optimized to reuse an existing image , done this way
//for now to get it working
- Ref<Image> image;
- image.instantiate();
- image->create(4, 4, false, Image::FORMAT_RGBA8);
+ Ref<Image> image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8);
image->fill(Color(1, 0, 1, 1));
Vector<Ref<Image>> images;
@@ -780,6 +806,7 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
#ifdef GLES_OVER_GL
// OpenGL 3.3 supports glGetTexImage which is faster and simpler than glReadPixels.
+ // It also allows for reading compressed textures, mipmaps, and more formats.
Vector<uint8_t> data;
int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, texture->real_format, texture->mipmaps > 1);
@@ -810,16 +837,71 @@ Ref<Image> TextureStorage::texture_2d_get(RID p_texture) const {
data.resize(data_size);
ERR_FAIL_COND_V(data.size() == 0, Ref<Image>());
- Ref<Image> image;
- image.instantiate();
- image->create(texture->width, texture->height, texture->mipmaps > 1, texture->real_format, data);
+ Ref<Image> image = Image::create_from_data(texture->width, texture->height, texture->mipmaps > 1, texture->real_format, data);
ERR_FAIL_COND_V(image->is_empty(), Ref<Image>());
if (texture->format != texture->real_format) {
image->convert(texture->format);
}
#else
- // Support for Web and Mobile will come later.
- Ref<Image> image;
+
+ Vector<uint8_t> data;
+
+ // On web and mobile we always read an RGBA8 image with no mipmaps.
+ int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false);
+
+ data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers
+ uint8_t *w = data.ptrw();
+
+ GLuint temp_framebuffer;
+ glGenFramebuffers(1, &temp_framebuffer);
+
+ GLuint temp_color_texture;
+ glGenTextures(1, &temp_color_texture);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, temp_framebuffer);
+
+ glBindTexture(GL_TEXTURE_2D, temp_color_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, temp_color_texture, 0);
+
+ glDepthMask(GL_FALSE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_BLEND);
+ glDepthFunc(GL_LEQUAL);
+ glColorMask(1, 1, 1, 1);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture->tex_id);
+
+ glViewport(0, 0, texture->alloc_width, texture->alloc_height);
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ CopyEffects::get_singleton()->copy_to_rect(Rect2i(0, 0, 1.0, 1.0));
+
+ glReadPixels(0, 0, texture->alloc_width, texture->alloc_height, GL_RGBA, GL_UNSIGNED_BYTE, &w[0]);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glDeleteTextures(1, &temp_color_texture);
+ glDeleteFramebuffers(1, &temp_framebuffer);
+
+ data.resize(data_size);
+
+ ERR_FAIL_COND_V(data.size() == 0, Ref<Image>());
+ Ref<Image> image = Image::create_from_data(texture->width, texture->height, false, Image::FORMAT_RGBA8, data);
+ ERR_FAIL_COND_V(image->is_empty(), Ref<Image>());
+
+ if (texture->format != Image::FORMAT_RGBA8) {
+ image->convert(texture->format);
+ }
+
+ if (texture->mipmaps > 1) {
+ image->generate_mipmaps();
+ }
+
#endif
#ifdef TOOLS_ENABLED
@@ -873,7 +955,7 @@ void TextureStorage::texture_replace(RID p_texture, RID p_by_texture) {
//delete last, so proxies can be updated
texture_owner.free(p_by_texture);
- //decal_atlas_mark_dirty_on_texture(p_texture);
+ texture_atlas_mark_dirty_on_texture(p_texture);
}
void TextureStorage::texture_set_size_override(RID p_texture, int p_width, int p_height) {
@@ -968,6 +1050,10 @@ Size2 TextureStorage::texture_size_with_proxy(RID p_texture) {
}
}
+RID TextureStorage::texture_get_rd_texture_rid(RID p_texture, bool p_srgb) const {
+ return RID();
+}
+
void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer) {
Texture *texture = texture_owner.get_or_null(p_texture);
@@ -1007,7 +1093,7 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image,
img->resize_to_po2(false);
}
- GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : GL_TEXTURE_2D;
+ GLenum blit_target = (texture->target == GL_TEXTURE_CUBE_MAP) ? _cube_side_enum[p_layer] : texture->target;
Vector<uint8_t> read = img->get_data();
@@ -1064,7 +1150,11 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref<Image> &p_image,
glCompressedTexImage2D(blit_target, i, internal_format, bw, bh, 0, size, &read[ofs]);
} else {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]);
+ if (texture->target == GL_TEXTURE_2D_ARRAY) {
+ glTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 0, format, type, &read[ofs]);
+ } else {
+ glTexImage2D(blit_target, i, internal_format, w, h, 0, format, type, &read[ofs]);
+ }
}
tsize += size;
@@ -1141,6 +1231,217 @@ RID TextureStorage::texture_create_radiance_cubemap(RID p_source, int p_resoluti
return RID();
}
+/* TEXTURE ATLAS API */
+
+void TextureStorage::texture_add_to_texture_atlas(RID p_texture) {
+ if (!texture_atlas.textures.has(p_texture)) {
+ TextureAtlas::Texture t;
+ t.users = 1;
+ texture_atlas.textures[p_texture] = t;
+ texture_atlas.dirty = true;
+ } else {
+ TextureAtlas::Texture *t = texture_atlas.textures.getptr(p_texture);
+ t->users++;
+ }
+}
+
+void TextureStorage::texture_remove_from_texture_atlas(RID p_texture) {
+ TextureAtlas::Texture *t = texture_atlas.textures.getptr(p_texture);
+ ERR_FAIL_COND(!t);
+ t->users--;
+ if (t->users == 0) {
+ texture_atlas.textures.erase(p_texture);
+ // Do not mark it dirty, there is no need to since it remains working.
+ }
+}
+
+void TextureStorage::texture_atlas_mark_dirty_on_texture(RID p_texture) {
+ if (texture_atlas.textures.has(p_texture)) {
+ texture_atlas.dirty = true; // Mark it dirty since it was most likely modified.
+ }
+}
+
+void TextureStorage::texture_atlas_remove_texture(RID p_texture) {
+ if (texture_atlas.textures.has(p_texture)) {
+ texture_atlas.textures.erase(p_texture);
+ // There is not much a point of making it dirty, texture can be removed next time the atlas is updated.
+ }
+}
+
+GLuint TextureStorage::texture_atlas_get_texture() const {
+ return texture_atlas.texture;
+}
+
+void TextureStorage::update_texture_atlas() {
+ CopyEffects *copy_effects = CopyEffects::get_singleton();
+ ERR_FAIL_NULL(copy_effects);
+
+ if (!texture_atlas.dirty) {
+ return; //nothing to do
+ }
+
+ texture_atlas.dirty = false;
+
+ if (texture_atlas.texture != 0) {
+ glDeleteTextures(1, &texture_atlas.texture);
+ texture_atlas.texture = 0;
+ glDeleteFramebuffers(1, &texture_atlas.framebuffer);
+ texture_atlas.framebuffer = 0;
+ }
+
+ const int border = 2;
+
+ if (texture_atlas.textures.size()) {
+ //generate atlas
+ Vector<TextureAtlas::SortItem> itemsv;
+ itemsv.resize(texture_atlas.textures.size());
+ int base_size = 8;
+
+ int idx = 0;
+
+ for (const KeyValue<RID, TextureAtlas::Texture> &E : texture_atlas.textures) {
+ TextureAtlas::SortItem &si = itemsv.write[idx];
+
+ Texture *src_tex = get_texture(E.key);
+
+ si.size.width = (src_tex->width / border) + 1;
+ si.size.height = (src_tex->height / border) + 1;
+ si.pixel_size = Size2i(src_tex->width, src_tex->height);
+
+ if (base_size < si.size.width) {
+ base_size = nearest_power_of_2_templated(si.size.width);
+ }
+
+ si.texture = E.key;
+ idx++;
+ }
+
+ //sort items by size
+ itemsv.sort();
+
+ //attempt to create atlas
+ int item_count = itemsv.size();
+ TextureAtlas::SortItem *items = itemsv.ptrw();
+
+ int atlas_height = 0;
+
+ while (true) {
+ Vector<int> v_offsetsv;
+ v_offsetsv.resize(base_size);
+
+ int *v_offsets = v_offsetsv.ptrw();
+ memset(v_offsets, 0, sizeof(int) * base_size);
+
+ int max_height = 0;
+
+ for (int i = 0; i < item_count; i++) {
+ //best fit
+ TextureAtlas::SortItem &si = items[i];
+ int best_idx = -1;
+ int best_height = 0x7FFFFFFF;
+ for (int j = 0; j <= base_size - si.size.width; j++) {
+ int height = 0;
+ for (int k = 0; k < si.size.width; k++) {
+ int h = v_offsets[k + j];
+ if (h > height) {
+ height = h;
+ if (height > best_height) {
+ break; //already bad
+ }
+ }
+ }
+
+ if (height < best_height) {
+ best_height = height;
+ best_idx = j;
+ }
+ }
+
+ //update
+ for (int k = 0; k < si.size.width; k++) {
+ v_offsets[k + best_idx] = best_height + si.size.height;
+ }
+
+ si.pos.x = best_idx;
+ si.pos.y = best_height;
+
+ if (si.pos.y + si.size.height > max_height) {
+ max_height = si.pos.y + si.size.height;
+ }
+ }
+
+ if (max_height <= base_size * 2) {
+ atlas_height = max_height;
+ break; //good ratio, break;
+ }
+
+ base_size *= 2;
+ }
+
+ texture_atlas.size.width = base_size * border;
+ texture_atlas.size.height = nearest_power_of_2_templated(atlas_height * border);
+
+ for (int i = 0; i < item_count; i++) {
+ TextureAtlas::Texture *t = texture_atlas.textures.getptr(items[i].texture);
+ t->uv_rect.position = items[i].pos * border + Vector2i(border / 2, border / 2);
+ t->uv_rect.size = items[i].pixel_size;
+
+ t->uv_rect.position /= Size2(texture_atlas.size);
+ t->uv_rect.size /= Size2(texture_atlas.size);
+ }
+ } else {
+ texture_atlas.size.width = 4;
+ texture_atlas.size.height = 4;
+ }
+
+ { // Atlas Texture initialize.
+ // TODO validate texture atlas size with maximum texture size
+ glGenTextures(1, &texture_atlas.texture);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, texture_atlas.texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texture_atlas.size.width, texture_atlas.size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+
+ glGenFramebuffers(1, &texture_atlas.framebuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, texture_atlas.framebuffer);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_atlas.texture, 0);
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ glDeleteFramebuffers(1, &texture_atlas.framebuffer);
+ texture_atlas.framebuffer = 0;
+ glDeleteTextures(1, &texture_atlas.texture);
+ texture_atlas.texture = 0;
+ WARN_PRINT("Could not create texture atlas, status: " + get_framebuffer_error(status));
+ return;
+ }
+ glViewport(0, 0, texture_atlas.size.width, texture_atlas.size.height);
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ }
+
+ glDisable(GL_BLEND);
+
+ if (texture_atlas.textures.size()) {
+ for (const KeyValue<RID, TextureAtlas::Texture> &E : texture_atlas.textures) {
+ TextureAtlas::Texture *t = texture_atlas.textures.getptr(E.key);
+ Texture *src_tex = get_texture(E.key);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, src_tex->tex_id);
+ copy_effects->copy_to_rect(t->uv_rect);
+ }
+ }
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
/* DECAL API */
RID TextureStorage::decal_allocate() {
@@ -1181,6 +1482,18 @@ AABB TextureStorage::decal_get_aabb(RID p_decal) const {
return AABB();
}
+/* DECAL INSTANCE API */
+
+RID TextureStorage::decal_instance_create(RID p_decal) {
+ return RID();
+}
+
+void TextureStorage::decal_instance_free(RID p_decal_instance) {
+}
+
+void TextureStorage::decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) {
+}
+
/* RENDER TARGET API */
GLuint TextureStorage::system_fbo = 0;
@@ -1197,9 +1510,11 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
return;
}
+ Config *config = Config::get_singleton();
+
rt->color_internal_format = rt->is_transparent ? GL_RGBA8 : GL_RGB10_A2;
rt->color_format = GL_RGBA;
- rt->color_type = rt->is_transparent ? GL_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV;
+ rt->color_type = rt->is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV;
rt->image_format = Image::FORMAT_RGBA8;
glDisable(GL_SCISSOR_TEST);
@@ -1207,31 +1522,74 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
glDepthMask(GL_FALSE);
{
- /* Front FBO */
+ Texture *texture;
+ bool use_multiview = rt->view_count > 1 && config->multiview_supported;
+ GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
- Texture *texture = get_texture(rt->texture);
- ERR_FAIL_COND(!texture);
+ /* Front FBO */
- // framebuffer
glGenFramebuffers(1, &rt->fbo);
glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo);
// color
- glGenTextures(1, &rt->color);
- glBindTexture(GL_TEXTURE_2D, rt->color);
+ if (rt->overridden.color.is_valid()) {
+ texture = get_texture(rt->overridden.color);
+ ERR_FAIL_COND(!texture);
- glTexImage2D(GL_TEXTURE_2D, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr);
+ rt->color = texture->tex_id;
+ rt->size = Size2i(texture->width, texture->height);
+ } else {
+ texture = get_texture(rt->texture);
+ ERR_FAIL_COND(!texture);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glGenTextures(1, &rt->color);
+ glBindTexture(texture_target, rt->color);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ if (use_multiview) {
+ glTexImage3D(texture_target, 0, rt->color_internal_format, rt->size.x, rt->size.y, rt->view_count, 0, rt->color_format, rt->color_type, nullptr);
+ } else {
+ glTexImage2D(texture_target, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr);
+ }
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
+ glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+ if (use_multiview) {
+ glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, rt->view_count);
+ } else {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0);
+ }
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ // depth
+ if (rt->overridden.depth.is_valid()) {
+ texture = get_texture(rt->overridden.depth);
+ ERR_FAIL_COND(!texture);
+ rt->depth = texture->tex_id;
+ } else {
+ glGenTextures(1, &rt->depth);
+ glBindTexture(texture_target, rt->depth);
+
+ if (use_multiview) {
+ glTexImage3D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, rt->view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
+ } else {
+ glTexImage2D(texture_target, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
+ }
+
+ glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+ if (use_multiview) {
+ glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, rt->depth, 0, 0, rt->view_count);
+ } else {
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, rt->depth, 0);
+ }
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
glDeleteFramebuffers(1, &rt->fbo);
glDeleteTextures(1, &rt->color);
@@ -1239,25 +1597,38 @@ void TextureStorage::_update_render_target(RenderTarget *rt) {
rt->size.x = 0;
rt->size.y = 0;
rt->color = 0;
- texture->tex_id = 0;
- texture->active = false;
+ rt->depth = 0;
+ if (rt->overridden.color.is_null()) {
+ texture->tex_id = 0;
+ texture->active = false;
+ }
WARN_PRINT("Could not create render target, status: " + get_framebuffer_error(status));
return;
}
- texture->format = rt->image_format;
- texture->real_format = rt->image_format;
- texture->type = Texture::TYPE_2D;
- texture->target = GL_TEXTURE_2D;
- texture->gl_format_cache = rt->color_format;
- texture->gl_type_cache = GL_UNSIGNED_BYTE;
- texture->gl_internal_format_cache = rt->color_internal_format;
- texture->tex_id = rt->color;
- texture->width = rt->size.x;
- texture->alloc_width = rt->size.x;
- texture->height = rt->size.y;
- texture->alloc_height = rt->size.y;
- texture->active = true;
+ if (rt->overridden.color.is_valid()) {
+ texture->is_render_target = true;
+ } else {
+ texture->format = rt->image_format;
+ texture->real_format = rt->image_format;
+ texture->target = texture_target;
+ if (rt->view_count > 1 && config->multiview_supported) {
+ texture->type = Texture::TYPE_LAYERED;
+ texture->layers = rt->view_count;
+ } else {
+ texture->type = Texture::TYPE_2D;
+ texture->layers = 1;
+ }
+ texture->gl_format_cache = rt->color_format;
+ texture->gl_type_cache = GL_UNSIGNED_BYTE;
+ texture->gl_internal_format_cache = rt->color_internal_format;
+ texture->tex_id = rt->color;
+ texture->width = rt->size.x;
+ texture->alloc_width = rt->size.x;
+ texture->height = rt->size.y;
+ texture->alloc_height = rt->size.y;
+ texture->active = true;
+ }
}
glClearColor(0, 0, 0, 0);
@@ -1325,35 +1696,32 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) {
if (rt->fbo) {
glDeleteFramebuffers(1, &rt->fbo);
- glDeleteTextures(1, &rt->color);
rt->fbo = 0;
+ }
+
+ if (rt->overridden.color.is_null()) {
+ glDeleteTextures(1, &rt->color);
rt->color = 0;
}
- /*
- if (rt->external.fbo != 0) {
- // free this
- glDeleteFramebuffers(1, &rt->external.fbo);
- // clean up our texture
- Texture *t = get_texture(rt->external.texture);
- t->alloc_height = 0;
- t->alloc_width = 0;
- t->width = 0;
- t->height = 0;
- t->active = false;
- texture_free(rt->external.texture);
- memdelete(t);
+ if (rt->overridden.depth.is_null()) {
+ glDeleteTextures(1, &rt->depth);
+ rt->depth = 0;
+ }
- rt->external.fbo = 0;
+ if (rt->texture.is_valid()) {
+ Texture *tex = get_texture(rt->texture);
+ tex->alloc_height = 0;
+ tex->alloc_width = 0;
+ tex->width = 0;
+ tex->height = 0;
+ tex->active = false;
}
- */
- Texture *tex = get_texture(rt->texture);
- tex->alloc_height = 0;
- tex->alloc_width = 0;
- tex->width = 0;
- tex->height = 0;
- tex->active = false;
+ if (rt->overridden.color.is_valid()) {
+ Texture *tex = get_texture(rt->overridden.color);
+ tex->is_render_target = false;
+ }
if (rt->backbuffer_fbo != 0) {
glDeleteFramebuffers(1, &rt->backbuffer_fbo);
@@ -1361,6 +1729,16 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) {
rt->backbuffer = 0;
rt->backbuffer_fbo = 0;
}
+ _render_target_clear_sdf(rt);
+}
+
+void TextureStorage::_clear_render_target_overridden_fbo_cache(RenderTarget *rt) {
+ // Dispose of the cached fbo's and the allocated textures
+ for (KeyValue<uint32_t, RenderTarget::RTOverridden::FBOCacheEntry> &E : rt->overridden.fbo_cache) {
+ glDeleteTextures(E.value.allocated_textures.size(), E.value.allocated_textures.ptr());
+ glDeleteFramebuffers(1, &E.value.fbo);
+ }
+ rt->overridden.fbo_cache.clear();
}
RID TextureStorage::render_target_create() {
@@ -1381,11 +1759,14 @@ RID TextureStorage::render_target_create() {
void TextureStorage::render_target_free(RID p_rid) {
RenderTarget *rt = render_target_owner.get_or_null(p_rid);
_clear_render_target(rt);
+ _clear_render_target_overridden_fbo_cache(rt);
Texture *t = get_texture(rt->texture);
if (t) {
t->is_render_target = false;
- texture_free(rt->texture);
+ if (rt->overridden.color.is_null()) {
+ texture_free(rt->texture);
+ }
//memdelete(t);
}
render_target_owner.free(p_rid);
@@ -1398,132 +1779,126 @@ void TextureStorage::render_target_set_position(RID p_render_target, int p_x, in
rt->position = Point2i(p_x, p_y);
}
+Point2i TextureStorage::render_target_get_position(RID p_render_target) const {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, Point2i());
+
+ return rt->position;
+};
+
void TextureStorage::render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_COND(!rt);
- if (p_width == rt->size.x && p_height == rt->size.y) {
+ if (p_width == rt->size.x && p_height == rt->size.y && p_view_count == rt->view_count) {
+ return;
+ }
+ if (rt->overridden.color.is_valid()) {
return;
}
_clear_render_target(rt);
rt->size = Size2i(p_width, p_height);
+ rt->view_count = p_view_count;
_update_render_target(rt);
}
// TODO: convert to Size2i internally
-Size2i TextureStorage::render_target_get_size(RID p_render_target) {
+Size2i TextureStorage::render_target_get_size(RID p_render_target) const {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
- ERR_FAIL_COND_V(!rt, Size2());
+ ERR_FAIL_COND_V(!rt, Size2i());
return rt->size;
}
-RID TextureStorage::render_target_get_texture(RID p_render_target) {
+void TextureStorage::render_target_set_override(RID p_render_target, RID p_color_texture, RID p_depth_texture, RID p_velocity_texture) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
- ERR_FAIL_COND_V(!rt, RID());
+ ERR_FAIL_COND(!rt);
+ ERR_FAIL_COND(rt->direct_to_screen);
- if (rt->external.fbo == 0) {
- return rt->texture;
- } else {
- return rt->external.texture;
+ rt->overridden.velocity = p_velocity_texture;
+
+ if (rt->overridden.color == p_color_texture && rt->overridden.depth == p_depth_texture) {
+ return;
}
-}
-void TextureStorage::render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) {
- RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
- ERR_FAIL_COND(!rt);
+ if (p_color_texture.is_null() && p_depth_texture.is_null()) {
+ _clear_render_target(rt);
+ rt->overridden.is_overridden = false;
+ rt->overridden.color = RID();
+ rt->overridden.depth = RID();
+ rt->size = Size2i();
+ _clear_render_target_overridden_fbo_cache(rt);
+ return;
+ }
- if (p_texture_id == 0) {
- if (rt->external.fbo != 0) {
- // free this
- glDeleteFramebuffers(1, &rt->external.fbo);
+ if (!rt->overridden.is_overridden) {
+ _clear_render_target(rt);
+ }
- // and this
- if (rt->external.depth != 0) {
- glDeleteRenderbuffers(1, &rt->external.depth);
- }
+ rt->overridden.color = p_color_texture;
+ rt->overridden.depth = p_depth_texture;
+ rt->overridden.is_overridden = true;
- // clean up our texture
- Texture *t = get_texture(rt->external.texture);
- t->alloc_height = 0;
- t->alloc_width = 0;
- t->width = 0;
- t->height = 0;
- t->active = false;
- texture_free(rt->external.texture);
- //memdelete(t);
-
- rt->external.fbo = 0;
- rt->external.color = 0;
- rt->external.depth = 0;
- }
- } else {
- Texture *t;
-
- if (rt->external.fbo == 0) {
- // create our fbo
- glGenFramebuffers(1, &rt->external.fbo);
- glBindFramebuffer(GL_FRAMEBUFFER, rt->external.fbo);
-
- // allocate a texture
- t = memnew(Texture);
-
- t->type = Texture::TYPE_2D;
- t->width = 0;
- t->height = 0;
- t->alloc_height = 0;
- t->alloc_width = 0;
- t->format = Image::FORMAT_RGBA8;
- t->target = GL_TEXTURE_2D;
- t->gl_format_cache = 0;
- t->gl_internal_format_cache = 0;
- t->gl_type_cache = 0;
- t->total_data_size = 0;
- t->mipmaps = 1;
- t->active = true;
- t->tex_id = 0;
- t->render_target = rt;
- t->is_render_target = true;
-
- //rt->external.texture = make_rid(t);
+ uint32_t hash_key = hash_murmur3_one_64(p_color_texture.get_id());
+ hash_key = hash_murmur3_one_64(p_depth_texture.get_id(), hash_key);
+ hash_key = hash_fmix32(hash_key);
- } else {
- // bind our frame buffer
- glBindFramebuffer(GL_FRAMEBUFFER, rt->external.fbo);
+ RBMap<uint32_t, RenderTarget::RTOverridden::FBOCacheEntry>::Element *cache;
+ if ((cache = rt->overridden.fbo_cache.find(hash_key)) != nullptr) {
+ rt->fbo = cache->get().fbo;
+ rt->size = cache->get().size;
+ rt->texture = p_color_texture;
+ return;
+ }
- // find our texture
- t = get_texture(rt->external.texture);
- }
+ _update_render_target(rt);
- // set our texture
- t->tex_id = p_texture_id;
- rt->external.color = p_texture_id;
+ RenderTarget::RTOverridden::FBOCacheEntry new_entry;
+ new_entry.fbo = rt->fbo;
+ new_entry.size = rt->size;
+ // Keep track of any textures we had to allocate because they weren't overridden.
+ if (p_color_texture.is_null()) {
+ new_entry.allocated_textures.push_back(rt->color);
+ }
+ if (p_depth_texture.is_null()) {
+ new_entry.allocated_textures.push_back(rt->depth);
+ }
+ rt->overridden.fbo_cache.insert(hash_key, new_entry);
+}
- // size shouldn't be different
- t->width = rt->size.x;
- t->height = rt->size.y;
- t->alloc_height = rt->size.x;
- t->alloc_width = rt->size.y;
+RID TextureStorage::render_target_get_override_color(RID p_render_target) const {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, RID());
- // Switch our texture on our frame buffer
- {
- // set our texture as the destination for our framebuffer
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_texture_id, 0);
- }
+ return rt->overridden.color;
+}
- // check status and unbind
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
+RID TextureStorage::render_target_get_override_depth(RID p_render_target) const {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, RID());
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- WARN_PRINT("framebuffer fail, status: " + get_framebuffer_error(status));
- }
+ return rt->overridden.depth;
+}
+
+RID TextureStorage::render_target_get_override_velocity(RID p_render_target) const {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, RID());
- ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE);
+ return rt->overridden.velocity;
+}
+
+RID TextureStorage::render_target_get_texture(RID p_render_target) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, RID());
+
+ if (rt->overridden.color.is_valid()) {
+ return rt->overridden.color;
}
+
+ return rt->texture;
}
void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_transparent) {
@@ -1532,8 +1907,17 @@ void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_t
rt->is_transparent = p_transparent;
- _clear_render_target(rt);
- _update_render_target(rt);
+ if (rt->overridden.color.is_null()) {
+ _clear_render_target(rt);
+ _update_render_target(rt);
+ }
+}
+
+bool TextureStorage::render_target_get_transparent(RID p_render_target) const {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, false);
+
+ return rt->is_transparent;
}
void TextureStorage::render_target_set_direct_to_screen(RID p_render_target, bool p_direct_to_screen) {
@@ -1547,10 +1931,22 @@ void TextureStorage::render_target_set_direct_to_screen(RID p_render_target, boo
// those functions change how they operate depending on the value of DIRECT_TO_SCREEN
_clear_render_target(rt);
rt->direct_to_screen = p_direct_to_screen;
+ if (rt->direct_to_screen) {
+ rt->overridden.color = RID();
+ rt->overridden.depth = RID();
+ rt->overridden.velocity = RID();
+ }
_update_render_target(rt);
}
-bool TextureStorage::render_target_was_used(RID p_render_target) {
+bool TextureStorage::render_target_get_direct_to_screen(RID p_render_target) const {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, false);
+
+ return rt->direct_to_screen;
+}
+
+bool TextureStorage::render_target_was_used(RID p_render_target) const {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_COND_V(!rt, false);
@@ -1564,6 +1960,27 @@ void TextureStorage::render_target_clear_used(RID p_render_target) {
rt->used_in_frame = false;
}
+void TextureStorage::render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND(!rt);
+ if (p_msaa == rt->msaa) {
+ return;
+ }
+
+ WARN_PRINT("2D MSAA is not yet supported for GLES3.");
+
+ _clear_render_target(rt);
+ rt->msaa = p_msaa;
+ _update_render_target(rt);
+}
+
+RS::ViewportMSAA TextureStorage::render_target_get_msaa(RID p_render_target) const {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, RS::VIEWPORT_MSAA_DISABLED);
+
+ return rt->msaa;
+}
+
void TextureStorage::render_target_request_clear(RID p_render_target, const Color &p_clear_color) {
RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
ERR_FAIL_COND(!rt);
@@ -1594,19 +2011,291 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) {
if (!rt->clear_requested) {
return;
}
+ glBindFramebuffer(GL_FRAMEBUFFER, rt->fbo);
glClearBufferfv(GL_COLOR, 0, rt->clear_color.components);
rt->clear_requested = false;
+ glBindFramebuffer(GL_FRAMEBUFFER, system_fbo);
}
void TextureStorage::render_target_set_sdf_size_and_scale(RID p_render_target, RS::ViewportSDFOversize p_size, RS::ViewportSDFScale p_scale) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND(!rt);
+ if (rt->sdf_oversize == p_size && rt->sdf_scale == p_scale) {
+ return;
+ }
+
+ rt->sdf_oversize = p_size;
+ rt->sdf_scale = p_scale;
+
+ _render_target_clear_sdf(rt);
+}
+
+Rect2i TextureStorage::_render_target_get_sdf_rect(const RenderTarget *rt) const {
+ Size2i margin;
+ int scale;
+ switch (rt->sdf_oversize) {
+ case RS::VIEWPORT_SDF_OVERSIZE_100_PERCENT: {
+ scale = 100;
+ } break;
+ case RS::VIEWPORT_SDF_OVERSIZE_120_PERCENT: {
+ scale = 120;
+ } break;
+ case RS::VIEWPORT_SDF_OVERSIZE_150_PERCENT: {
+ scale = 150;
+ } break;
+ case RS::VIEWPORT_SDF_OVERSIZE_200_PERCENT: {
+ scale = 200;
+ } break;
+ default: {
+ }
+ }
+
+ margin = (rt->size * scale / 100) - rt->size;
+
+ Rect2i r(Vector2i(), rt->size);
+ r.position -= margin;
+ r.size += margin * 2;
+
+ return r;
}
Rect2i TextureStorage::render_target_get_sdf_rect(RID p_render_target) const {
- return Rect2i();
+ const RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, Rect2i());
+
+ return _render_target_get_sdf_rect(rt);
}
void TextureStorage::render_target_mark_sdf_enabled(RID p_render_target, bool p_enabled) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND(!rt);
+
+ rt->sdf_enabled = p_enabled;
+}
+
+bool TextureStorage::render_target_is_sdf_enabled(RID p_render_target) const {
+ const RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, false);
+
+ return rt->sdf_enabled;
+}
+
+GLuint TextureStorage::render_target_get_sdf_texture(RID p_render_target) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, 0);
+ if (rt->sdf_texture_read == 0) {
+ Texture *texture = texture_owner.get_or_null(default_gl_textures[DEFAULT_GL_TEXTURE_BLACK]);
+ return texture->tex_id;
+ }
+
+ return rt->sdf_texture_read;
+}
+
+void TextureStorage::_render_target_allocate_sdf(RenderTarget *rt) {
+ ERR_FAIL_COND(rt->sdf_texture_write_fb != 0);
+
+ Size2i size = _render_target_get_sdf_rect(rt).size;
+
+ glGenTextures(1, &rt->sdf_texture_write);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_write);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, size.width, size.height, 0, GL_RED, GL_UNSIGNED_BYTE, nullptr);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glGenFramebuffers(1, &rt->sdf_texture_write_fb);
+ glBindFramebuffer(GL_FRAMEBUFFER, rt->sdf_texture_write_fb);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->sdf_texture_write, 0);
+
+ int scale;
+ switch (rt->sdf_scale) {
+ case RS::VIEWPORT_SDF_SCALE_100_PERCENT: {
+ scale = 100;
+ } break;
+ case RS::VIEWPORT_SDF_SCALE_50_PERCENT: {
+ scale = 50;
+ } break;
+ case RS::VIEWPORT_SDF_SCALE_25_PERCENT: {
+ scale = 25;
+ } break;
+ default: {
+ scale = 100;
+ } break;
+ }
+
+ rt->process_size = size * scale / 100;
+ rt->process_size.x = MAX(rt->process_size.x, 1);
+ rt->process_size.y = MAX(rt->process_size.y, 1);
+
+ glGenTextures(2, rt->sdf_texture_process);
+ glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_process[0]);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16I, rt->process_size.width, rt->process_size.height, 0, GL_RG_INTEGER, GL_SHORT, nullptr);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_process[1]);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16I, rt->process_size.width, rt->process_size.height, 0, GL_RG_INTEGER, GL_SHORT, nullptr);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glGenTextures(1, &rt->sdf_texture_read);
+ glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_read);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, rt->process_size.width, rt->process_size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+void TextureStorage::_render_target_clear_sdf(RenderTarget *rt) {
+ if (rt->sdf_texture_write_fb != 0) {
+ glDeleteTextures(1, &rt->sdf_texture_read);
+ glDeleteTextures(1, &rt->sdf_texture_write);
+ glDeleteTextures(2, rt->sdf_texture_process);
+ glDeleteFramebuffers(1, &rt->sdf_texture_write_fb);
+ rt->sdf_texture_read = 0;
+ rt->sdf_texture_write = 0;
+ rt->sdf_texture_process[0] = 0;
+ rt->sdf_texture_process[1] = 0;
+ rt->sdf_texture_write_fb = 0;
+ }
+}
+
+GLuint TextureStorage::render_target_get_sdf_framebuffer(RID p_render_target) {
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND_V(!rt, 0);
+
+ if (rt->sdf_texture_write_fb == 0) {
+ _render_target_allocate_sdf(rt);
+ }
+
+ return rt->sdf_texture_write_fb;
+}
+void TextureStorage::render_target_sdf_process(RID p_render_target) {
+ CopyEffects *copy_effects = CopyEffects::get_singleton();
+
+ RenderTarget *rt = render_target_owner.get_or_null(p_render_target);
+ ERR_FAIL_COND(!rt);
+ ERR_FAIL_COND(rt->sdf_texture_write_fb == 0);
+
+ Rect2i r = _render_target_get_sdf_rect(rt);
+
+ Size2i size = r.size;
+ int32_t shift = 0;
+
+ bool shrink = false;
+
+ switch (rt->sdf_scale) {
+ case RS::VIEWPORT_SDF_SCALE_50_PERCENT: {
+ size[0] >>= 1;
+ size[1] >>= 1;
+ shift = 1;
+ shrink = true;
+ } break;
+ case RS::VIEWPORT_SDF_SCALE_25_PERCENT: {
+ size[0] >>= 2;
+ size[1] >>= 2;
+ shift = 2;
+ shrink = true;
+ } break;
+ default: {
+ };
+ }
+
+ GLuint temp_fb;
+ glGenFramebuffers(1, &temp_fb);
+ glBindFramebuffer(GL_FRAMEBUFFER, temp_fb);
+
+ // Load
+ CanvasSdfShaderGLES3::ShaderVariant variant = shrink ? CanvasSdfShaderGLES3::MODE_LOAD_SHRINK : CanvasSdfShaderGLES3::MODE_LOAD;
+ bool success = sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant);
+ if (!success) {
+ return;
+ }
+
+ sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::BASE_SIZE, r.size, sdf_shader.shader_version, variant);
+ sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SIZE, size, sdf_shader.shader_version, variant);
+ sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, 0, sdf_shader.shader_version, variant);
+ sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SHIFT, shift, sdf_shader.shader_version, variant);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_write);
+
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->sdf_texture_process[0], 0);
+ glViewport(0, 0, size.width, size.height);
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(0, 0, size.width, size.height);
+
+ copy_effects->draw_screen_triangle();
+
+ // Process
+
+ int stride = nearest_power_of_2_templated(MAX(size.width, size.height) / 2);
+
+ variant = CanvasSdfShaderGLES3::MODE_PROCESS;
+ success = sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant);
+ if (!success) {
+ return;
+ }
+
+ sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::BASE_SIZE, r.size, sdf_shader.shader_version, variant);
+ sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SIZE, size, sdf_shader.shader_version, variant);
+ sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, stride, sdf_shader.shader_version, variant);
+ sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SHIFT, shift, sdf_shader.shader_version, variant);
+
+ bool swap = false;
+
+ //jumpflood
+ while (stride > 0) {
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->sdf_texture_process[swap ? 0 : 1], 0);
+ glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_process[swap ? 1 : 0]);
+
+ sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, stride, sdf_shader.shader_version, variant);
+
+ copy_effects->draw_screen_triangle();
+
+ stride /= 2;
+ swap = !swap;
+ }
+
+ // Store
+ variant = shrink ? CanvasSdfShaderGLES3::MODE_STORE_SHRINK : CanvasSdfShaderGLES3::MODE_STORE;
+ success = sdf_shader.shader.version_bind_shader(sdf_shader.shader_version, variant);
+ if (!success) {
+ return;
+ }
+
+ sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::BASE_SIZE, r.size, sdf_shader.shader_version, variant);
+ sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SIZE, size, sdf_shader.shader_version, variant);
+ sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::STRIDE, stride, sdf_shader.shader_version, variant);
+ sdf_shader.shader.version_set_uniform(CanvasSdfShaderGLES3::SHIFT, shift, sdf_shader.shader_version, variant);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->sdf_texture_read, 0);
+ glBindTexture(GL_TEXTURE_2D, rt->sdf_texture_process[swap ? 1 : 0]);
+
+ copy_effects->draw_screen_triangle();
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER, system_fbo);
+ glDeleteFramebuffers(1, &temp_fb);
+ glDisable(GL_SCISSOR_TEST);
}
void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps) {