diff options
Diffstat (limited to 'drivers')
55 files changed, 2481 insertions, 1178 deletions
diff --git a/drivers/SCsub b/drivers/SCsub index dd81fc645c..6cfcb1d18c 100644 --- a/drivers/SCsub +++ b/drivers/SCsub @@ -24,6 +24,7 @@ SConscript("winmidi/SCsub") # Graphics drivers if env["vulkan"]: + SConscript("spirv-reflect/SCsub") SConscript("vulkan/SCsub") if env["opengl3"]: SConscript("gl_context/SCsub") @@ -31,7 +32,6 @@ if env["opengl3"]: # Core dependencies SConscript("png/SCsub") -SConscript("spirv-reflect/SCsub") env.add_source_files(env.drivers_sources, "*.cpp") diff --git a/drivers/gles3/effects/copy_effects.cpp b/drivers/gles3/effects/copy_effects.cpp index de0181f887..92c29a4264 100644 --- a/drivers/gles3/effects/copy_effects.cpp +++ b/drivers/gles3/effects/copy_effects.cpp @@ -114,7 +114,7 @@ CopyEffects::~CopyEffects() { copy.shader.version_free(copy.shader_version); } -void CopyEffects::copy_to_rect(const Rect2i &p_rect) { +void CopyEffects::copy_to_rect(const Rect2 &p_rect) { copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION); copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION); glBindVertexArray(quad_array); diff --git a/drivers/gles3/effects/copy_effects.h b/drivers/gles3/effects/copy_effects.h index b863e76579..7f16b4e0f3 100644 --- a/drivers/gles3/effects/copy_effects.h +++ b/drivers/gles3/effects/copy_effects.h @@ -61,7 +61,7 @@ public: ~CopyEffects(); // These functions assume that a framebuffer and texture are bound already. They only manage the shader, uniforms, and vertex array. - void copy_to_rect(const Rect2i &p_rect); + void copy_to_rect(const Rect2 &p_rect); void copy_screen(); void bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region); void set_color(const Color &p_color, const Rect2i &p_region); diff --git a/drivers/gles3/environment/fog.cpp b/drivers/gles3/environment/fog.cpp index 02d88f6871..8587c5f9f7 100644 --- a/drivers/gles3/environment/fog.cpp +++ b/drivers/gles3/environment/fog.cpp @@ -43,7 +43,7 @@ RID Fog::fog_volume_allocate() { void Fog::fog_volume_initialize(RID p_rid) { } -void Fog::fog_free(RID p_rid) { +void Fog::fog_volume_free(RID p_rid) { } void Fog::fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) { diff --git a/drivers/gles3/environment/fog.h b/drivers/gles3/environment/fog.h index 350eb459cf..fcde7399dd 100644 --- a/drivers/gles3/environment/fog.h +++ b/drivers/gles3/environment/fog.h @@ -46,7 +46,7 @@ public: virtual RID fog_volume_allocate() override; virtual void fog_volume_initialize(RID p_rid) override; - virtual void fog_free(RID p_rid) override; + virtual void fog_volume_free(RID p_rid) override; virtual void fog_volume_set_shape(RID p_fog_volume, RS::FogVolumeShape p_shape) override; virtual void fog_volume_set_extents(RID p_fog_volume, const Vector3 &p_extents) override; diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 0ffede0992..5d0edda6c5 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -115,7 +115,7 @@ void RasterizerCanvasGLES3::_update_transform_to_mat4(const Transform3D &p_trans p_mat4[15] = 1; } -void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) { +void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_light_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); @@ -124,9 +124,196 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ // Clear out any state that may have been left from the 3D pass. reset_canvas(); - // TODO: Setup Directional Lights + if (state.canvas_instance_data_buffers[state.current_buffer].fence != GLsync()) { + GLint syncStatus; + glGetSynciv(state.canvas_instance_data_buffers[state.current_buffer].fence, GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); + if (syncStatus == GL_UNSIGNALED) { + // If older than 2 frames, wait for sync OpenGL can have up to 3 frames in flight, any more and we need to sync anyway. + if (state.canvas_instance_data_buffers[state.current_buffer].last_frame_used < RSG::rasterizer->get_frame_number() - 2) { +#ifndef WEB_ENABLED + // On web, we do nothing as the glSubBufferData will force a sync anyway and WebGL does not like waiting. + glClientWaitSync(state.canvas_instance_data_buffers[state.current_buffer].fence, 0, 100000000); // wait for up to 100ms +#endif + } else { + // Used in last frame or frame before that. OpenGL can get up to two frames behind, so these buffers may still be in use + // Allocate a new buffer and use that. + _allocate_instance_data_buffer(); + } + } else { + // Already finished all rendering commands, we can use it. + state.canvas_instance_data_buffers[state.current_buffer].last_frame_used = RSG::rasterizer->get_frame_number(); + glDeleteSync(state.canvas_instance_data_buffers[state.current_buffer].fence); + state.canvas_instance_data_buffers[state.current_buffer].fence = GLsync(); + } + } + + //setup directional lights if exist + + uint32_t light_count = 0; + uint32_t directional_light_count = 0; + { + Light *l = p_directional_light_list; + uint32_t index = 0; + + while (l) { + if (index == data.max_lights_per_render) { + l->render_index_cache = -1; + l = l->next_ptr; + continue; + } + + CanvasLight *clight = canvas_light_owner.get_or_null(l->light_internal); + if (!clight) { //unused or invalid texture + l->render_index_cache = -1; + l = l->next_ptr; + ERR_CONTINUE(!clight); + } + + Vector2 canvas_light_dir = l->xform_cache.columns[1].normalized(); + + state.light_uniforms[index].position[0] = -canvas_light_dir.x; + state.light_uniforms[index].position[1] = -canvas_light_dir.y; + + //_update_transform_2d_to_mat2x4(clight->shadow.directional_xform, state.light_uniforms[index].shadow_matrix); + + state.light_uniforms[index].height = l->height; //0..1 here + + for (int i = 0; i < 4; i++) { + state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255)); + state.light_uniforms[index].color[i] = l->color[i]; + } + + state.light_uniforms[index].color[3] = l->energy; //use alpha for energy, so base color can go separate + + /* + if (state.shadow_fb.is_valid()) { + state.light_uniforms[index].shadow_pixel_size = (1.0 / state.shadow_texture_size) * (1.0 + l->shadow_smooth); + state.light_uniforms[index].shadow_z_far_inv = 1.0 / clight->shadow.z_far; + state.light_uniforms[index].shadow_y_ofs = clight->shadow.y_offset; + } else { + state.light_uniforms[index].shadow_pixel_size = 1.0; + state.light_uniforms[index].shadow_z_far_inv = 1.0; + state.light_uniforms[index].shadow_y_ofs = 0; + } + */ + + state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT; + state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT; + /* + if (clight->shadow.enabled) { + state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW; + } + */ + + l->render_index_cache = index; + + index++; + l = l->next_ptr; + } + + light_count = index; + directional_light_count = light_count; + state.using_directional_lights = directional_light_count > 0; + } + + //setup lights if exist + + { + Light *l = p_light_list; + uint32_t index = light_count; + + while (l) { + if (index == data.max_lights_per_render) { + l->render_index_cache = -1; + l = l->next_ptr; + continue; + } + + CanvasLight *clight = canvas_light_owner.get_or_null(l->light_internal); + if (!clight) { //unused or invalid texture + l->render_index_cache = -1; + l = l->next_ptr; + ERR_CONTINUE(!clight); + } + Transform2D to_light_xform = (p_canvas_transform * l->light_shader_xform).affine_inverse(); + + Vector2 canvas_light_pos = p_canvas_transform.xform(l->xform.get_origin()); //convert light position to canvas coordinates, as all computation is done in canvas coords to avoid precision loss + state.light_uniforms[index].position[0] = canvas_light_pos.x; + state.light_uniforms[index].position[1] = canvas_light_pos.y; + + _update_transform_2d_to_mat2x4(to_light_xform, state.light_uniforms[index].matrix); + _update_transform_2d_to_mat2x4(l->xform_cache.affine_inverse(), state.light_uniforms[index].shadow_matrix); + + state.light_uniforms[index].height = l->height * (p_canvas_transform.columns[0].length() + p_canvas_transform.columns[1].length()) * 0.5; //approximate height conversion to the canvas size, since all calculations are done in canvas coords to avoid precision loss + for (int i = 0; i < 4; i++) { + state.light_uniforms[index].shadow_color[i] = uint8_t(CLAMP(int32_t(l->shadow_color[i] * 255.0), 0, 255)); + state.light_uniforms[index].color[i] = l->color[i]; + } + + state.light_uniforms[index].color[3] = l->energy; //use alpha for energy, so base color can go separate + + /* + if (state.shadow_fb.is_valid()) { + state.light_uniforms[index].shadow_pixel_size = (1.0 / state.shadow_texture_size) * (1.0 + l->shadow_smooth); + state.light_uniforms[index].shadow_z_far_inv = 1.0 / clight->shadow.z_far; + state.light_uniforms[index].shadow_y_ofs = clight->shadow.y_offset; + } else { + state.light_uniforms[index].shadow_pixel_size = 1.0; + state.light_uniforms[index].shadow_z_far_inv = 1.0; + state.light_uniforms[index].shadow_y_ofs = 0; + } + */ + state.light_uniforms[index].flags = l->blend_mode << LIGHT_FLAGS_BLEND_SHIFT; + state.light_uniforms[index].flags |= l->shadow_filter << LIGHT_FLAGS_FILTER_SHIFT; + /* + if (clight->shadow.enabled) { + state.light_uniforms[index].flags |= LIGHT_FLAGS_HAS_SHADOW; + } + */ + + if (clight->texture.is_valid()) { + Rect2 atlas_rect = GLES3::TextureStorage::get_singleton()->texture_atlas_get_texture_rect(clight->texture); + state.light_uniforms[index].atlas_rect[0] = atlas_rect.position.x; + state.light_uniforms[index].atlas_rect[1] = atlas_rect.position.y; + state.light_uniforms[index].atlas_rect[2] = atlas_rect.size.width; + state.light_uniforms[index].atlas_rect[3] = atlas_rect.size.height; + + } else { + state.light_uniforms[index].atlas_rect[0] = 0; + state.light_uniforms[index].atlas_rect[1] = 0; + state.light_uniforms[index].atlas_rect[2] = 0; + state.light_uniforms[index].atlas_rect[3] = 0; + } + + l->render_index_cache = index; + + index++; + l = l->next_ptr; + } + + light_count = index; + } + + if (light_count > 0) { + glBindBufferBase(GL_UNIFORM_BUFFER, LIGHT_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].light_ubo); + +#ifdef WEB_ENABLED + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(LightUniform) * light_count, state.light_uniforms); +#else + // On Desktop and mobile we map the memory without synchronizing for maximum speed. + void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(LightUniform) * light_count, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + memcpy(ubo, state.light_uniforms, sizeof(LightUniform) * light_count); + glUnmapBuffer(GL_UNIFORM_BUFFER); +#endif - // TODO: Setup lights + GLuint texture_atlas = texture_storage->texture_atlas_get_texture(); + if (texture_atlas == 0) { + GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE)); + texture_atlas = tex->tex_id; + } + glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 2); + glBindTexture(GL_TEXTURE_2D, texture_atlas); + } { //update canvas state uniform buffer @@ -155,13 +342,12 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ state_buffer.screen_pixel_size[0] = 1.0 / render_target_size.x; state_buffer.screen_pixel_size[1] = 1.0 / render_target_size.y; - // TODO: temporary, this should be set at the top of this function glViewport(0, 0, render_target_size.x, render_target_size.y); state_buffer.time = state.time; state_buffer.use_pixel_snap = p_snap_2d_vertices_to_pixel; - state_buffer.directional_light_count = 0; //directional_light_count; + state_buffer.directional_light_count = directional_light_count; Vector2 canvas_scale = p_canvas_transform.get_scale(); @@ -180,7 +366,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ state_buffer.sdf_to_tex[3] = -sdf_tex_rect.position.y / sdf_tex_rect.size.height; state_buffer.tex_to_sdf = 1.0 / ((canvas_scale.x + canvas_scale.y) * 0.5); - glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_LOCATION, state.canvas_state_buffer); + glBindBufferBase(GL_UNIFORM_BUFFER, BASE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].state_ubo); glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), &state_buffer, GL_STREAM_DRAW); GLuint global_buffer = material_storage->global_shader_parameters_get_uniform_buffer(); @@ -207,6 +393,8 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ Item *ci = p_item_list; Item *canvas_group_owner = nullptr; + uint32_t starting_index = 0; + while (ci) { if (ci->copy_back_buffer && canvas_group_owner == nullptr) { backbuffer_copy = true; @@ -245,14 +433,15 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ if (ci->canvas_group_owner != nullptr) { if (canvas_group_owner == nullptr) { // Canvas group begins here, render until before this item - - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false); item_count = 0; - Rect2i group_rect = ci->canvas_group_owner->global_rect_cache; - - if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_OPAQUE) { + if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) { + Rect2i group_rect = ci->canvas_group_owner->global_rect_cache; texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false); + if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + items[item_count++] = ci->canvas_group_owner; + } } else if (!backbuffer_cleared) { texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0)); backbuffer_cleared = true; @@ -271,7 +460,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } if (ci == canvas_group_owner) { - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, true); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, true); item_count = 0; if (ci->canvas_group->blur_mipmaps) { @@ -279,12 +468,14 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ } canvas_group_owner = nullptr; + // Backbuffer is dirty now and needs to be re-cleared if another CanvasGroup needs it. + backbuffer_cleared = false; } if (backbuffer_copy) { //render anything pending, including clearing if no items - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false); item_count = 0; texture_storage->render_target_copy_to_back_buffer(p_to_render_target, back_buffer_rect, backbuffer_gen_mipmaps); @@ -306,7 +497,7 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ items[item_count++] = ci; if (!ci->next || item_count == MAX_RENDER_ITEMS - 1) { - _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list); + _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false); //then reset item_count = 0; } @@ -318,153 +509,124 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ RenderingServerDefault::redraw_request(); } + state.canvas_instance_data_buffers[state.current_buffer].fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + // Clear out state used in 2D pass reset_canvas(); + state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); } -void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer) { - GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); +void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, uint32_t &r_last_index, bool p_to_backbuffer) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); - Item *current_clip = nullptr; - - Transform2D canvas_transform_inverse = p_canvas_transform_inverse; canvas_begin(p_to_render_target, p_to_backbuffer); - RID prev_material; + if (p_item_count <= 0) { + // Nothing to draw, just call canvas_begin() to clear the render target and return. + return; + } + uint32_t index = 0; - GLES3::CanvasShaderData::BlendMode last_blend_mode = GLES3::CanvasShaderData::BLEND_MODE_MIX; - Color last_blend_color; - GLES3::CanvasShaderData *shader_data_cache = nullptr; + Item *current_clip = nullptr; + + // Record Batches. + // First item always forms its own batch. + bool batch_broken = false; + _new_batch(batch_broken, index); - state.current_tex = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE); - state.current_tex_ptr = nullptr; - state.current_normal = RID(); - state.current_specular = RID(); - state.canvas_texscreen_used = false; - state.current_shader_version = state.canvas_shader_default_version; + // Override the start position and index as we want to start from where we finished off last time. + state.canvas_instance_batches[state.current_batch_index].start = r_last_index * sizeof(InstanceData); + index = 0; + _align_instance_data_buffer(index); for (int i = 0; i < p_item_count; i++) { Item *ci = items[i]; - if (current_clip != ci->final_clip_owner) { - _render_batch(index); - + if (ci->final_clip_owner != state.canvas_instance_batches[state.current_batch_index].clip) { + _new_batch(batch_broken, index); + state.canvas_instance_batches[state.current_batch_index].clip = ci->final_clip_owner; current_clip = ci->final_clip_owner; - //setup clip - if (current_clip) { - glEnable(GL_SCISSOR_TEST); - glScissor(current_clip->final_clip_rect.position.x, current_clip->final_clip_rect.position.y, current_clip->final_clip_rect.size.x, current_clip->final_clip_rect.size.y); - } else { - glDisable(GL_SCISSOR_TEST); - } } RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; - - if (material.is_null() && ci->canvas_group != nullptr) { - material = default_canvas_group_material; + if (ci->canvas_group != nullptr) { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + if (!p_to_backbuffer) { + material = default_clip_children_material; + } + } else { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) { + material = default_clip_children_material; + } else { + material = default_canvas_group_material; + } + } } - if (material != prev_material) { - _render_batch(index); + GLES3::CanvasShaderData *shader_data_cache = nullptr; + if (material != state.canvas_instance_batches[state.current_batch_index].material) { + _new_batch(batch_broken, index); + GLES3::CanvasMaterialData *material_data = nullptr; if (material.is_valid()) { material_data = static_cast<GLES3::CanvasMaterialData *>(material_storage->material_get_data(material, RS::SHADER_CANVAS_ITEM)); } + shader_data_cache = nullptr; if (material_data) { if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { - // Bind uniform buffer and textures - material_data->bind_uniforms(); - state.current_shader_version = material_data->shader_data->version; shader_data_cache = material_data->shader_data; - } else { - state.current_shader_version = state.canvas_shader_default_version; - shader_data_cache = nullptr; } - } else { - state.current_shader_version = state.canvas_shader_default_version; - shader_data_cache = nullptr; } - prev_material = material; + + state.canvas_instance_batches[state.current_batch_index].material = material; + state.canvas_instance_batches[state.current_batch_index].material_data = material_data; } GLES3::CanvasShaderData::BlendMode blend_mode = shader_data_cache ? shader_data_cache->blend_mode : GLES3::CanvasShaderData::BLEND_MODE_MIX; - _render_item(p_to_render_target, ci, canvas_transform_inverse, current_clip, p_lights, index, blend_mode, last_blend_mode, last_blend_color); + _record_item_commands(ci, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken); } - // Render last command - _render_batch(index); -} - -void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, uint32_t &r_index, GLES3::CanvasShaderData::BlendMode p_blend_mode, GLES3::CanvasShaderData::BlendMode &r_last_blend_mode, Color &r_last_blend_color) { - // Used by Polygon and Mesh. - static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP }; - - RS::CanvasItemTextureFilter current_filter = state.default_filter; - RS::CanvasItemTextureRepeat current_repeat = state.default_repeat; - - if (p_item->texture_filter != RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT) { - current_filter = p_item->texture_filter; - } - - if (p_item->texture_repeat != RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT) { - current_repeat = p_item->texture_repeat; - } - - Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform; - Transform2D draw_transform; // Used by transform command - - Color base_color = p_item->final_modulate; - - uint32_t base_flags = 0; - - bool reclip = false; - - bool skipping = false; - - const Item::Command *c = p_item->commands; - while (c) { - if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) { - c = c->next; - continue; - } - - if (c->type != Item::Command::TYPE_MESH) { - // For Meshes, this gets updated below. - _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); - } - for (int i = 0; i < 4; i++) { - state.instance_data_array[r_index].modulation[i] = 0.0; - state.instance_data_array[r_index].ninepatch_margins[i] = 0.0; - state.instance_data_array[r_index].src_rect[i] = 0.0; - state.instance_data_array[r_index].dst_rect[i] = 0.0; - state.instance_data_array[r_index].lights[i] = uint32_t(0); - } - state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0; - state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0; + // Copy over all data needed for rendering. + glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_instance_data_buffers[state.current_buffer].ubo); +#ifdef WEB_ENABLED + glBufferSubData(GL_UNIFORM_BUFFER, r_last_index * sizeof(InstanceData), sizeof(InstanceData) * index, state.instance_data_array); +#else + // On Desktop and mobile we map the memory without synchronizing for maximum speed. + void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, r_last_index * sizeof(InstanceData), index * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); + memcpy(ubo, state.instance_data_array, index * sizeof(InstanceData)); + glUnmapBuffer(GL_UNIFORM_BUFFER); +#endif - state.instance_data_array[r_index].pad[0] = 0.0; - state.instance_data_array[r_index].pad[1] = 0.0; + glDisable(GL_SCISSOR_TEST); + current_clip = nullptr; - state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config + GLES3::CanvasShaderData::BlendMode last_blend_mode = GLES3::CanvasShaderData::BLEND_MODE_MIX; - GLES3::CanvasShaderData::BlendMode blend_mode = p_blend_mode; - Color blend_color; + state.current_tex = RID(); - if (c->type == Item::Command::TYPE_RECT) { - const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); - if (rect->flags & CANVAS_RECT_LCD) { - blend_mode = GLES3::CanvasShaderData::BLEND_MODE_LCD; - blend_color = rect->modulate; + for (uint32_t i = 0; i <= state.current_batch_index; i++) { + //setup clip + if (current_clip != state.canvas_instance_batches[i].clip) { + current_clip = state.canvas_instance_batches[i].clip; + if (current_clip) { + glEnable(GL_SCISSOR_TEST); + glScissor(current_clip->final_clip_rect.position.x, current_clip->final_clip_rect.position.y, current_clip->final_clip_rect.size.x, current_clip->final_clip_rect.size.y); + } else { + glDisable(GL_SCISSOR_TEST); } } - if (r_last_blend_mode != blend_mode || r_last_blend_color != blend_color) { - _render_batch(r_index); + GLES3::CanvasMaterialData *material_data = state.canvas_instance_batches[i].material_data; + CanvasShaderGLES3::ShaderVariant variant = state.canvas_instance_batches[i].shader_variant; + uint64_t specialization = 0; + specialization |= uint64_t(state.canvas_instance_batches[i].lights_disabled); + _bind_material(material_data, variant, specialization); - if (r_last_blend_mode == GLES3::CanvasShaderData::BLEND_MODE_DISABLED) { + GLES3::CanvasShaderData::BlendMode blend_mode = state.canvas_instance_batches[i].blend_mode; + + if (last_blend_mode != blend_mode) { + if (last_blend_mode == GLES3::CanvasShaderData::BLEND_MODE_DISABLED) { // re-enable it glEnable(GL_BLEND); } else if (blend_mode == GLES3::CanvasShaderData::BLEND_MODE_DISABLED) { @@ -475,7 +637,6 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item switch (blend_mode) { case GLES3::CanvasShaderData::BLEND_MODE_DISABLED: { // Nothing to do here. - } break; case GLES3::CanvasShaderData::BLEND_MODE_LCD: { glBlendEquation(GL_FUNC_ADD); @@ -484,6 +645,7 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item } else { glBlendFuncSeparate(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_ZERO, GL_ONE); } + Color blend_color = state.canvas_instance_batches[state.current_batch_index].blend_color; glBlendColor(blend_color.r, blend_color.g, blend_color.b, blend_color.a); } break; @@ -532,32 +694,149 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item } break; } - r_last_blend_mode = blend_mode; - r_last_blend_color = blend_color; + last_blend_mode = blend_mode; + } + + _render_batch(p_lights, i); + } + + state.current_batch_index = 0; + state.canvas_instance_batches.clear(); + r_last_index += index; +} + +void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken) { + RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter; + + if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) { + _new_batch(r_batch_broken, r_index); + + state.canvas_instance_batches[state.current_batch_index].filter = texture_filter; + } + + RenderingServer::CanvasItemTextureRepeat texture_repeat = p_item->texture_repeat == RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT ? state.default_repeat : p_item->texture_repeat; + + if (texture_repeat != state.canvas_instance_batches[state.current_batch_index].repeat) { + _new_batch(r_batch_broken, r_index); + + state.canvas_instance_batches[state.current_batch_index].repeat = texture_repeat; + } + + Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform; + Transform2D draw_transform; // Used by transform command + + Color base_color = p_item->final_modulate; + uint32_t base_flags = 0; + Size2 texpixel_size; + + bool reclip = false; + + bool skipping = false; + + // TODO: consider making lights a per-batch property and then baking light operations in the shader for better performance. + uint32_t lights[4] = { 0, 0, 0, 0 }; + + uint16_t light_count = 0; + + { + Light *light = p_lights; + + while (light) { + if (light->render_index_cache >= 0 && p_item->light_mask & light->item_mask && p_item->z_final >= light->z_min && p_item->z_final <= light->z_max && p_item->global_rect_cache.intersects_transformed(light->xform_cache, light->rect_cache)) { + uint32_t light_index = light->render_index_cache; + lights[light_count >> 2] |= light_index << ((light_count & 3) * 8); + + light_count++; + + if (light_count == data.max_lights_per_item) { + break; + } + } + light = light->next_ptr; + } + + base_flags |= light_count << FLAGS_LIGHT_COUNT_SHIFT; + } + + bool lights_disabled = light_count == 0 && !state.using_directional_lights; + + if (lights_disabled != state.canvas_instance_batches[state.current_batch_index].lights_disabled) { + _new_batch(r_batch_broken, r_index); + state.canvas_instance_batches[state.current_batch_index].lights_disabled = lights_disabled; + } + + const Item::Command *c = p_item->commands; + while (c) { + if (skipping && c->type != Item::Command::TYPE_ANIMATION_SLICE) { + c = c->next; + continue; + } + + if (c->type != Item::Command::TYPE_MESH) { + // For Meshes, this gets updated below. + _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); + } + + // Zero out most fields. + for (int i = 0; i < 4; i++) { + state.instance_data_array[r_index].modulation[i] = 0.0; + state.instance_data_array[r_index].ninepatch_margins[i] = 0.0; + state.instance_data_array[r_index].src_rect[i] = 0.0; + state.instance_data_array[r_index].dst_rect[i] = 0.0; + state.instance_data_array[r_index].lights[i] = uint32_t(0); + } + state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0; + state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0; + + state.instance_data_array[r_index].pad[0] = 0.0; + state.instance_data_array[r_index].pad[1] = 0.0; + + state.instance_data_array[r_index].lights[0] = lights[0]; + state.instance_data_array[r_index].lights[1] = lights[1]; + state.instance_data_array[r_index].lights[2] = lights[2]; + state.instance_data_array[r_index].lights[3] = lights[3]; + + state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config + + Color blend_color; + if (c->type == Item::Command::TYPE_RECT) { + const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); + if (rect->flags & CANVAS_RECT_LCD) { + p_blend_mode = GLES3::CanvasShaderData::BLEND_MODE_LCD; + blend_color = rect->modulate * base_color; + } + } + + if (p_blend_mode != state.canvas_instance_batches[state.current_batch_index].blend_mode || blend_color != state.canvas_instance_batches[state.current_batch_index].blend_color) { + _new_batch(r_batch_broken, r_index); + state.canvas_instance_batches[state.current_batch_index].blend_mode = p_blend_mode; + state.canvas_instance_batches[state.current_batch_index].blend_color = blend_color; } switch (c->type) { case Item::Command::TYPE_RECT: { const Item::CommandRect *rect = static_cast<const Item::CommandRect *>(c); - if (rect->flags & CANVAS_RECT_TILE) { - current_repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; + if (rect->flags & CANVAS_RECT_TILE && state.canvas_instance_batches[state.current_batch_index].repeat != RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED) { + _new_batch(r_batch_broken, r_index); + state.canvas_instance_batches[state.current_batch_index].repeat = RenderingServer::CanvasItemTextureRepeat::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED; } - if (rect->texture != state.current_tex || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_RECT) { - _render_batch(r_index); - - state.current_primitive_points = 0; - state.current_command = Item::Command::TYPE_RECT; + if (rect->texture != state.canvas_instance_batches[state.current_batch_index].tex || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_RECT) { + _new_batch(r_batch_broken, r_index); + state.canvas_instance_batches[state.current_batch_index].tex = rect->texture; + state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_RECT; + state.canvas_instance_batches[state.current_batch_index].command = c; + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_QUAD; } - _bind_canvas_texture(rect->texture, current_filter, current_repeat, r_index); - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_QUAD); + + _prepare_canvas_texture(rect->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); Rect2 src_rect; Rect2 dst_rect; if (rect->texture != RID()) { - src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * state.current_pixel_size, rect->source.size * state.current_pixel_size) : Rect2(0, 0, 1, 1); + src_rect = (rect->flags & CANVAS_RECT_REGION) ? Rect2(rect->source.position * texpixel_size, rect->source.size * texpixel_size) : Rect2(0, 0, 1, 1); dst_rect = Rect2(rect->rect.position, rect->rect.size); if (dst_rect.size.width < 0) { @@ -625,36 +904,32 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].dst_rect[2] = dst_rect.size.width; state.instance_data_array[r_index].dst_rect[3] = dst_rect.size.height; - r_index++; - if (r_index >= state.max_instances_per_batch - 1) { - _render_batch(r_index); - } + _add_to_batch(r_index, r_batch_broken); } break; case Item::Command::TYPE_NINEPATCH: { const Item::CommandNinePatch *np = static_cast<const Item::CommandNinePatch *>(c); - if (np->texture != state.current_tex || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_NINEPATCH) { - _render_batch(r_index); - - state.current_primitive_points = 0; - state.current_command = Item::Command::TYPE_NINEPATCH; + if (np->texture != state.canvas_instance_batches[state.current_batch_index].tex || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_NINEPATCH) { + _new_batch(r_batch_broken, r_index); + state.canvas_instance_batches[state.current_batch_index].tex = np->texture; + state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_NINEPATCH; + state.canvas_instance_batches[state.current_batch_index].command = c; + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_NINEPATCH; } - //bind textures - _bind_canvas_texture(np->texture, current_filter, current_repeat, r_index); - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_NINEPATCH); + _prepare_canvas_texture(np->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); Rect2 src_rect; Rect2 dst_rect(np->rect.position.x, np->rect.position.y, np->rect.size.x, np->rect.size.y); if (np->texture == RID()) { - state.current_pixel_size = Size2(1, 1); + texpixel_size = Size2(1, 1); src_rect = Rect2(0, 0, 1, 1); } else { if (np->source != Rect2()) { - src_rect = Rect2(np->source.position.x * state.current_pixel_size.width, np->source.position.y * state.current_pixel_size.height, np->source.size.x * state.current_pixel_size.width, np->source.size.y * state.current_pixel_size.height); + src_rect = Rect2(np->source.position.x * texpixel_size.width, np->source.position.y * texpixel_size.height, np->source.size.x * texpixel_size.width, np->source.size.y * texpixel_size.height); state.instance_data_array[r_index].color_texture_pixel_size[0] = 1.0 / np->source.size.width; state.instance_data_array[r_index].color_texture_pixel_size[1] = 1.0 / np->source.size.height; @@ -690,32 +965,26 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].ninepatch_margins[2] = np->margin[SIDE_RIGHT]; state.instance_data_array[r_index].ninepatch_margins[3] = np->margin[SIDE_BOTTOM]; - r_index++; - if (r_index >= state.max_instances_per_batch - 1) { - _render_batch(r_index); - } + _add_to_batch(r_index, r_batch_broken); // Restore if overridden. - state.instance_data_array[r_index].color_texture_pixel_size[0] = state.current_pixel_size.x; - state.instance_data_array[r_index].color_texture_pixel_size[1] = state.current_pixel_size.y; + state.instance_data_array[r_index].color_texture_pixel_size[0] = texpixel_size.x; + state.instance_data_array[r_index].color_texture_pixel_size[1] = texpixel_size.y; } break; case Item::Command::TYPE_POLYGON: { const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(c); - PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id); - ERR_CONTINUE(!pb); + // Polygon's can't be batched, so always create a new batch + _new_batch(r_batch_broken, r_index); - if (polygon->texture != state.current_tex || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_POLYGON) { - _render_batch(r_index); + state.canvas_instance_batches[state.current_batch_index].tex = polygon->texture; + state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_POLYGON; + state.canvas_instance_batches[state.current_batch_index].command = c; + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES; - state.current_primitive_points = 0; - state.current_command = Item::Command::TYPE_POLYGON; - } - _bind_canvas_texture(polygon->texture, current_filter, current_repeat, r_index); - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_ATTRIBUTES); + _prepare_canvas_texture(polygon->texture, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); - state.current_primitive = polygon->primitive; state.instance_data_array[r_index].modulation[0] = base_color.r; state.instance_data_array[r_index].modulation[1] = base_color.g; state.instance_data_array[r_index].modulation[2] = base_color.b; @@ -727,39 +996,20 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].ninepatch_margins[j] = 0; } - _bind_instance_data_buffer(1); - glBindVertexArray(pb->vertex_array); - - if (pb->color_disabled) { - glVertexAttrib4f(RS::ARRAY_COLOR, pb->color.r, pb->color.g, pb->color.b, pb->color.a); - } - - if (pb->index_buffer != 0) { - glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, nullptr); - } else { - glDrawArrays(prim[polygon->primitive], 0, pb->count); - } - glBindVertexArray(0); - state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - - state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); - - if (pb->color_disabled) { - // Reset so this doesn't pollute other draw calls. - glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0); - } + _add_to_batch(r_index, r_batch_broken); } break; case Item::Command::TYPE_PRIMITIVE: { const Item::CommandPrimitive *primitive = static_cast<const Item::CommandPrimitive *>(c); - if (state.current_primitive_points != primitive->point_count || state.current_command != Item::Command::TYPE_PRIMITIVE) { - _render_batch(r_index); - state.current_primitive_points = primitive->point_count; - state.current_command = Item::Command::TYPE_PRIMITIVE; + if (primitive->point_count != state.canvas_instance_batches[state.current_batch_index].primitive_points || state.canvas_instance_batches[state.current_batch_index].command_type != Item::Command::TYPE_PRIMITIVE) { + _new_batch(r_batch_broken, r_index); + state.canvas_instance_batches[state.current_batch_index].tex = RID(); + state.canvas_instance_batches[state.current_batch_index].primitive_points = primitive->point_count; + state.canvas_instance_batches[state.current_batch_index].command_type = Item::Command::TYPE_PRIMITIVE; + state.canvas_instance_batches[state.current_batch_index].command = c; + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_PRIMITIVE; } - _bind_canvas_texture(RID(), current_filter, current_repeat, r_index); - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_PRIMITIVE); for (uint32_t j = 0; j < MIN(3u, primitive->point_count); j++) { state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j].x; @@ -770,97 +1020,61 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); } - r_index++; + + _add_to_batch(r_index, r_batch_broken); + if (primitive->point_count == 4) { - // Reset base data + // Reset base data. _update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world); state.instance_data_array[r_index].color_texture_pixel_size[0] = 0.0; state.instance_data_array[r_index].color_texture_pixel_size[1] = 0.0; - state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index == 0 ? 0 : r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config + state.instance_data_array[r_index].flags = base_flags | (state.instance_data_array[r_index - 1].flags & (FLAGS_DEFAULT_NORMAL_MAP_USED | FLAGS_DEFAULT_SPECULAR_MAP_USED)); //reset on each command for sanity, keep canvastexture binding config for (uint32_t j = 0; j < 3; j++) { - //second half of triangle - state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j + 1].x; - state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j + 1].y; - state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j + 1].x; - state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j + 1].y; - Color col = primitive->colors[j + 1] * base_color; + int offset = j == 0 ? 0 : 1; + // Second triangle in the quad. Uses vertices 0, 2, 3. + state.instance_data_array[r_index].points[j * 2 + 0] = primitive->points[j + offset].x; + state.instance_data_array[r_index].points[j * 2 + 1] = primitive->points[j + offset].y; + state.instance_data_array[r_index].uvs[j * 2 + 0] = primitive->uvs[j + offset].x; + state.instance_data_array[r_index].uvs[j * 2 + 1] = primitive->uvs[j + offset].y; + Color col = primitive->colors[j + offset] * base_color; state.instance_data_array[r_index].colors[j * 2 + 0] = (uint32_t(Math::make_half_float(col.g)) << 16) | Math::make_half_float(col.r); state.instance_data_array[r_index].colors[j * 2 + 1] = (uint32_t(Math::make_half_float(col.a)) << 16) | Math::make_half_float(col.b); } - r_index++; - } - if (r_index >= state.max_instances_per_batch - 1) { - _render_batch(r_index); + + _add_to_batch(r_index, r_batch_broken); } } break; case Item::Command::TYPE_MESH: case Item::Command::TYPE_MULTIMESH: case Item::Command::TYPE_PARTICLES: { - GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); - RID mesh; - RID mesh_instance; - RID texture; - Color modulate(1, 1, 1, 1); - uint32_t instance_count = 1; - GLuint multimesh_buffer = 0; - uint32_t multimesh_stride = 0; - uint32_t multimesh_color_offset = 0; - bool multimesh_uses_color = false; - bool multimesh_uses_custom_data = false; + // Mesh's can't be batched, so always create a new batch + _new_batch(r_batch_broken, r_index); + Color modulate(1, 1, 1, 1); + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_ATTRIBUTES; if (c->type == Item::Command::TYPE_MESH) { const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(c); - mesh = m->mesh; - mesh_instance = m->mesh_instance; - texture = m->texture; - modulate = m->modulate; + state.canvas_instance_batches[state.current_batch_index].tex = m->texture; _update_transform_2d_to_mat2x3(base_transform * draw_transform * m->transform, state.instance_data_array[r_index].world); + modulate = m->modulate; } else if (c->type == Item::Command::TYPE_MULTIMESH) { const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(c); - RID multimesh = mm->multimesh; - mesh = mesh_storage->multimesh_get_mesh(multimesh); - texture = mm->texture; - - if (mesh_storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) { - break; - } - - instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh); - - if (instance_count == 0) { - break; + state.canvas_instance_batches[state.current_batch_index].tex = mm->texture; + uint32_t instance_count = GLES3::MeshStorage::get_singleton()->multimesh_get_instances_to_draw(mm->multimesh); + if (instance_count > 1) { + state.canvas_instance_batches[state.current_batch_index].shader_variant = CanvasShaderGLES3::MODE_INSTANCED; } - - multimesh_buffer = mesh_storage->multimesh_get_gl_buffer(multimesh); - multimesh_stride = mesh_storage->multimesh_get_stride(multimesh); - multimesh_color_offset = mesh_storage->multimesh_get_color_offset(multimesh); - multimesh_uses_color = mesh_storage->multimesh_uses_colors(multimesh); - multimesh_uses_custom_data = mesh_storage->multimesh_uses_custom_data(multimesh); + } else if (c->type == Item::Command::TYPE_PARTICLES) { + WARN_PRINT_ONCE("Particles not supported yet, sorry :("); } - // TODO: implement particles here - - if (mesh.is_null()) { - break; - } - - if (texture != state.current_tex || state.current_primitive_points != 0 || state.current_command != Item::Command::TYPE_PRIMITIVE) { - _render_batch(r_index); - state.current_primitive_points = 0; - state.current_command = c->type; - } - - _bind_canvas_texture(texture, current_filter, current_repeat, r_index); - if (instance_count == 1) { - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_ATTRIBUTES); - } else { - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.current_shader_version, CanvasShaderGLES3::MODE_INSTANCED); - } + state.canvas_instance_batches[state.current_batch_index].command = c; + state.canvas_instance_batches[state.current_batch_index].command_type = c->type; - uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh); + _prepare_canvas_texture(state.canvas_instance_batches[state.current_batch_index].tex, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size); state.instance_data_array[r_index].modulation[0] = base_color.r * modulate.r; state.instance_data_array[r_index].modulation[1] = base_color.g * modulate.g; @@ -872,75 +1086,9 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item state.instance_data_array[r_index].dst_rect[j] = 0; state.instance_data_array[r_index].ninepatch_margins[j] = 0; } - _bind_instance_data_buffer(1); - for (uint32_t j = 0; j < surf_count; j++) { - void *surface = mesh_storage->mesh_get_surface(mesh, j); - - RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface); - ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX); - - GLuint vertex_array_gl = 0; - GLuint index_array_gl = 0; - - uint32_t input_mask = 0; // 2D meshes always use the same vertex format - if (mesh_instance.is_valid()) { - mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array_gl); - } else { - mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, vertex_array_gl); - } - - index_array_gl = mesh_storage->mesh_surface_get_index_buffer(surface, 0); - bool use_index_buffer = false; - glBindVertexArray(vertex_array_gl); - if (index_array_gl != 0) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_gl); - use_index_buffer = true; - } - - if (instance_count > 1) { - // Bind instance buffers. - glBindBuffer(GL_ARRAY_BUFFER, multimesh_buffer); - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0)); - glVertexAttribDivisor(1, 1); - glEnableVertexAttribArray(2); - glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 4)); - glVertexAttribDivisor(2, 1); - - if (multimesh_uses_color || multimesh_uses_custom_data) { - glEnableVertexAttribArray(5); - glVertexAttribIPointer(5, 4, GL_UNSIGNED_INT, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(multimesh_color_offset * sizeof(float))); - glVertexAttribDivisor(5, 1); - } - } - - GLenum primitive_gl = prim[int(primitive)]; - if (instance_count == 1) { - if (use_index_buffer) { - glDrawElements(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0); - } else { - glDrawArrays(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface)); - } - } else { - if (use_index_buffer) { - glDrawElementsInstanced(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0, instance_count); - } else { - glDrawArraysInstanced(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), instance_count); - } - } - - state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - - state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - if (instance_count > 1) { - glDisableVertexAttribArray(5); - glDisableVertexAttribArray(6); - glDisableVertexAttribArray(7); - glDisableVertexAttribArray(8); - } - } + _add_to_batch(r_index, r_batch_broken); } break; + case Item::Command::TYPE_TRANSFORM: { const Item::CommandTransform *transform = static_cast<const Item::CommandTransform *>(c); draw_transform = transform->xform; @@ -950,30 +1098,30 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item const Item::CommandClipIgnore *ci = static_cast<const Item::CommandClipIgnore *>(c); if (current_clip) { if (ci->ignore != reclip) { + _new_batch(r_batch_broken, r_index); if (ci->ignore) { - glDisable(GL_SCISSOR_TEST); + state.canvas_instance_batches[state.current_batch_index].clip = nullptr; reclip = true; } else { - // Scissor area is already set - glEnable(GL_SCISSOR_TEST); + state.canvas_instance_batches[state.current_batch_index].clip = current_clip; reclip = false; } } } } break; + case Item::Command::TYPE_ANIMATION_SLICE: { - /* const Item::CommandAnimationSlice *as = static_cast<const Item::CommandAnimationSlice *>(c); - double current_time = RendererCompositorRD::singleton->get_total_time(); + double current_time = RSG::rasterizer->get_total_time(); double local_time = Math::fposmod(current_time - as->offset, as->animation_length); skipping = !(local_time >= as->slice_begin && local_time < as->slice_end); RenderingServerDefault::redraw_request(); // animation visible means redraw request - */ } break; } c = c->next; + r_batch_broken = false; } if (current_clip && reclip) { @@ -982,73 +1130,268 @@ void RasterizerCanvasGLES3::_render_item(RID p_render_target, const Item *p_item } } -void RasterizerCanvasGLES3::_render_batch(uint32_t &r_index) { - if (r_index > 0) { - _bind_instance_data_buffer(r_index); - glBindVertexArray(data.canvas_quad_array); - if (state.current_primitive_points == 0) { - glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, r_index); - } else { - static const GLenum prim[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLES }; - glDrawArraysInstanced(prim[state.current_primitive_points], 0, state.current_primitive_points, r_index); - } - glBindBuffer(GL_UNIFORM_BUFFER, 0); +void RasterizerCanvasGLES3::_render_batch(Light *p_lights, uint32_t p_index) { + ERR_FAIL_COND(!state.canvas_instance_batches[state.current_batch_index].command); - state.fences[state.current_buffer] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - state.current_buffer = (state.current_buffer + 1) % state.canvas_instance_data_buffers.size(); - //copy the new data into the base of the batch - for (int i = 0; i < 4; i++) { - state.instance_data_array[0].modulation[i] = state.instance_data_array[r_index].modulation[i]; - state.instance_data_array[0].ninepatch_margins[i] = state.instance_data_array[r_index].ninepatch_margins[i]; - state.instance_data_array[0].src_rect[i] = state.instance_data_array[r_index].src_rect[i]; - state.instance_data_array[0].dst_rect[i] = state.instance_data_array[r_index].dst_rect[i]; - state.instance_data_array[0].lights[i] = state.instance_data_array[r_index].lights[i]; - } - state.instance_data_array[0].flags = state.instance_data_array[r_index].flags; - state.instance_data_array[0].color_texture_pixel_size[0] = state.instance_data_array[r_index].color_texture_pixel_size[0]; - state.instance_data_array[0].color_texture_pixel_size[1] = state.instance_data_array[r_index].color_texture_pixel_size[1]; - - state.instance_data_array[0].pad[0] = state.instance_data_array[r_index].pad[0]; - state.instance_data_array[0].pad[1] = state.instance_data_array[r_index].pad[1]; - for (int i = 0; i < 6; i++) { - state.instance_data_array[0].world[i] = state.instance_data_array[r_index].world[i]; - } + // Used by Polygon and Mesh. + static const GLenum prim[5] = { GL_POINTS, GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP }; + + _bind_canvas_texture(state.canvas_instance_batches[p_index].tex, state.canvas_instance_batches[p_index].filter, state.canvas_instance_batches[p_index].repeat); - r_index = 0; + // Bind the region of the UBO used by this batch. + // If region exceeds the boundary of the UBO, just ignore. + uint32_t range_bytes = data.max_instances_per_batch * sizeof(InstanceData); + if (state.canvas_instance_batches[p_index].start >= (data.max_instances_per_ubo - 1) * sizeof(InstanceData)) { + return; + } else if (state.canvas_instance_batches[p_index].start >= (data.max_instances_per_ubo - data.max_instances_per_batch) * sizeof(InstanceData)) { + // If we have less than a full batch at the end, we can just draw it anyway. + // OpenGL will complain about the UBO being smaller than expected, but it should render fine. + range_bytes = (data.max_instances_per_ubo - 1) * sizeof(InstanceData) - state.canvas_instance_batches[p_index].start; + } + + uint32_t range_start = state.canvas_instance_batches[p_index].start; + glBindBufferRange(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer].ubo, range_start, range_bytes); + + switch (state.canvas_instance_batches[p_index].command_type) { + case Item::Command::TYPE_RECT: + case Item::Command::TYPE_NINEPATCH: { + glBindVertexArray(data.indexed_quad_array); + glDrawElements(GL_TRIANGLES, state.canvas_instance_batches[p_index].instance_count * 6, GL_UNSIGNED_INT, 0); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + glBindVertexArray(0); + + } break; + + case Item::Command::TYPE_POLYGON: { + const Item::CommandPolygon *polygon = static_cast<const Item::CommandPolygon *>(state.canvas_instance_batches[p_index].command); + + PolygonBuffers *pb = polygon_buffers.polygons.getptr(polygon->polygon.polygon_id); + ERR_FAIL_COND(!pb); + + glBindVertexArray(pb->vertex_array); + + if (pb->color_disabled && pb->color != Color(1.0, 1.0, 1.0, 1.0)) { + glVertexAttrib4f(RS::ARRAY_COLOR, pb->color.r, pb->color.g, pb->color.b, pb->color.a); + } + + if (pb->index_buffer != 0) { + glDrawElements(prim[polygon->primitive], pb->count, GL_UNSIGNED_INT, nullptr); + } else { + glDrawArrays(prim[polygon->primitive], 0, pb->count); + } + glBindVertexArray(0); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + if (pb->color_disabled && pb->color != Color(1.0, 1.0, 1.0, 1.0)) { + // Reset so this doesn't pollute other draw calls. + glVertexAttrib4f(RS::ARRAY_COLOR, 1.0, 1.0, 1.0, 1.0); + } + } break; + + case Item::Command::TYPE_PRIMITIVE: { + glBindVertexArray(data.canvas_quad_array); + const GLenum primitive[5] = { GL_POINTS, GL_POINTS, GL_LINES, GL_TRIANGLES, GL_TRIANGLES }; + int instance_count = state.canvas_instance_batches[p_index].instance_count; + if (instance_count > 1) { + glDrawArraysInstanced(primitive[state.canvas_instance_batches[p_index].primitive_points], 0, state.canvas_instance_batches[p_index].primitive_points, instance_count); + } else { + glDrawArrays(primitive[state.canvas_instance_batches[p_index].primitive_points], 0, state.canvas_instance_batches[p_index].primitive_points); + } + glBindBuffer(GL_UNIFORM_BUFFER, 0); + + } break; + + case Item::Command::TYPE_MESH: + case Item::Command::TYPE_MULTIMESH: + case Item::Command::TYPE_PARTICLES: { + GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); + RID mesh; + RID mesh_instance; + RID texture; + uint32_t instance_count = 1; + GLuint multimesh_buffer = 0; + uint32_t multimesh_stride = 0; + uint32_t multimesh_color_offset = 0; + bool multimesh_uses_color = false; + bool multimesh_uses_custom_data = false; + + if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_MESH) { + const Item::CommandMesh *m = static_cast<const Item::CommandMesh *>(state.canvas_instance_batches[p_index].command); + mesh = m->mesh; + mesh_instance = m->mesh_instance; + } else if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_MULTIMESH) { + const Item::CommandMultiMesh *mm = static_cast<const Item::CommandMultiMesh *>(state.canvas_instance_batches[p_index].command); + RID multimesh = mm->multimesh; + mesh = mesh_storage->multimesh_get_mesh(multimesh); + + if (mesh_storage->multimesh_get_transform_format(multimesh) != RS::MULTIMESH_TRANSFORM_2D) { + break; + } + + instance_count = mesh_storage->multimesh_get_instances_to_draw(multimesh); + + if (instance_count == 0) { + break; + } + + multimesh_buffer = mesh_storage->multimesh_get_gl_buffer(multimesh); + multimesh_stride = mesh_storage->multimesh_get_stride(multimesh); + multimesh_color_offset = mesh_storage->multimesh_get_color_offset(multimesh); + multimesh_uses_color = mesh_storage->multimesh_uses_colors(multimesh); + multimesh_uses_custom_data = mesh_storage->multimesh_uses_custom_data(multimesh); + } else if (state.canvas_instance_batches[p_index].command_type == Item::Command::TYPE_PARTICLES) { + // Do nothing for now. + } + + ERR_FAIL_COND(mesh.is_null()); + + uint32_t surf_count = mesh_storage->mesh_get_surface_count(mesh); + + for (uint32_t j = 0; j < surf_count; j++) { + void *surface = mesh_storage->mesh_get_surface(mesh, j); + + RS::PrimitiveType primitive = mesh_storage->mesh_surface_get_primitive(surface); + ERR_CONTINUE(primitive < 0 || primitive >= RS::PRIMITIVE_MAX); + + GLuint vertex_array_gl = 0; + GLuint index_array_gl = 0; + + uint32_t input_mask = 0; // 2D meshes always use the same vertex format + if (mesh_instance.is_valid()) { + mesh_storage->mesh_instance_surface_get_vertex_arrays_and_format(mesh_instance, j, input_mask, vertex_array_gl); + } else { + mesh_storage->mesh_surface_get_vertex_arrays_and_format(surface, input_mask, vertex_array_gl); + } + + index_array_gl = mesh_storage->mesh_surface_get_index_buffer(surface, 0); + bool use_index_buffer = false; + glBindVertexArray(vertex_array_gl); + if (index_array_gl != 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_array_gl); + use_index_buffer = true; + } + + if (instance_count > 1) { + // Bind instance buffers. + glBindBuffer(GL_ARRAY_BUFFER, multimesh_buffer); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(0)); + glVertexAttribDivisor(1, 1); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(4 * 4)); + glVertexAttribDivisor(2, 1); + + if (multimesh_uses_color || multimesh_uses_custom_data) { + glEnableVertexAttribArray(5); + glVertexAttribIPointer(5, 4, GL_UNSIGNED_INT, multimesh_stride * sizeof(float), CAST_INT_TO_UCHAR_PTR(multimesh_color_offset * sizeof(float))); + glVertexAttribDivisor(5, 1); + } + } + + GLenum primitive_gl = prim[int(primitive)]; + if (instance_count == 1) { + if (use_index_buffer) { + glDrawElements(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0); + } else { + glDrawArrays(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface)); + } + } else if (instance_count > 1) { + if (use_index_buffer) { + glDrawElementsInstanced(primitive_gl, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), mesh_storage->mesh_surface_get_index_type(surface), 0, instance_count); + } else { + glDrawArraysInstanced(primitive_gl, 0, mesh_storage->mesh_surface_get_vertices_drawn_count(surface), instance_count); + } + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + if (instance_count > 1) { + glDisableVertexAttribArray(5); + glDisableVertexAttribArray(6); + glDisableVertexAttribArray(7); + glDisableVertexAttribArray(8); + } + } + + } break; + case Item::Command::TYPE_TRANSFORM: + case Item::Command::TYPE_CLIP_IGNORE: + case Item::Command::TYPE_ANIMATION_SLICE: { + // Can ignore these as they only impact batch creation. + } break; } } -void RasterizerCanvasGLES3::_bind_instance_data_buffer(uint32_t p_max_index) { - if (p_max_index == 0) { +void RasterizerCanvasGLES3::_add_to_batch(uint32_t &r_index, bool &r_batch_broken) { + if (r_index >= data.max_instances_per_ubo - 1) { + WARN_PRINT_ONCE("Trying to draw too many items. Please increase maximum number of items in the project settings 'rendering/gl_compatibility/item_buffer_size'"); return; } - // If the previous operation is not done yet, allocate a new buffer - if (state.fences[state.current_buffer] != GLsync()) { - GLint syncStatus; - glGetSynciv(state.fences[state.current_buffer], GL_SYNC_STATUS, sizeof(GLint), nullptr, &syncStatus); - if (syncStatus == GL_UNSIGNALED) { - _allocate_instance_data_buffer(); + + if (state.canvas_instance_batches[state.current_batch_index].instance_count >= data.max_instances_per_batch) { + _new_batch(r_batch_broken, r_index); + } + + state.canvas_instance_batches[state.current_batch_index].instance_count++; + r_index++; +} + +void RasterizerCanvasGLES3::_new_batch(bool &r_batch_broken, uint32_t &r_index) { + if (state.canvas_instance_batches.size() == 0) { + state.canvas_instance_batches.push_back(Batch()); + return; + } + + if (r_batch_broken || state.canvas_instance_batches[state.current_batch_index].instance_count == 0) { + return; + } + + r_batch_broken = true; + + // Copy the properties of the current batch, we will manually update the things that changed. + Batch new_batch = state.canvas_instance_batches[state.current_batch_index]; + new_batch.instance_count = 0; + new_batch.start = state.canvas_instance_batches[state.current_batch_index].start + state.canvas_instance_batches[state.current_batch_index].instance_count * sizeof(InstanceData); + + state.current_batch_index++; + state.canvas_instance_batches.push_back(new_batch); + _align_instance_data_buffer(r_index); +} + +void RasterizerCanvasGLES3::_bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization) { + if (p_material_data) { + if (p_material_data->shader_data->version.is_valid() && p_material_data->shader_data->valid) { + // Bind uniform buffer and textures + p_material_data->bind_uniforms(); + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(p_material_data->shader_data->version, p_variant, p_specialization); } else { - glDeleteSync(state.fences[state.current_buffer]); + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant, p_specialization); } + } else { + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, p_variant, p_specialization); } - - glBindBufferBase(GL_UNIFORM_BUFFER, INSTANCE_UNIFORM_LOCATION, state.canvas_instance_data_buffers[state.current_buffer]); -#ifdef WEB_ENABLED - //WebGL 2.0 does not support mapping buffers, so use slow glBufferSubData instead - glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData) * p_max_index, state.instance_data_array); -#else - void *ubo = glMapBufferRange(GL_UNIFORM_BUFFER, 0, sizeof(InstanceData) * p_max_index, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT); - memcpy(ubo, state.instance_data_array, sizeof(InstanceData) * p_max_index); - glUnmapBuffer(GL_UNIFORM_BUFFER); -#endif } RID RasterizerCanvasGLES3::light_create() { - return RID(); + CanvasLight canvas_light; + return canvas_light_owner.make_rid(canvas_light); } void RasterizerCanvasGLES3::light_set_texture(RID p_rid, RID p_texture) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_COND(!cl); + if (cl->texture == p_texture) { + return; + } + if (cl->texture.is_valid()) { + texture_storage->texture_remove_from_texture_atlas(cl->texture); + } + cl->texture = p_texture; + + if (cl->texture.is_valid()) { + texture_storage->texture_add_to_texture_atlas(cl->texture); + } } void RasterizerCanvasGLES3::light_set_use_shadow(RID p_rid, bool p_enable) { @@ -1077,6 +1420,14 @@ void RasterizerCanvasGLES3::set_shadow_texture_size(int p_size) { } bool RasterizerCanvasGLES3::free(RID p_rid) { + if (canvas_light_owner.owns(p_rid)) { + CanvasLight *cl = canvas_light_owner.get_or_null(p_rid); + ERR_FAIL_COND_V(!cl, false); + canvas_light_owner.free(p_rid); + } else { + return false; + } + return true; } @@ -1100,12 +1451,12 @@ void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backb glBindTexture(GL_TEXTURE_2D, render_target->backbuffer); } - if (render_target->is_transparent) { + if (render_target->is_transparent || p_to_backbuffer) { state.transparent_render_target = true; glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } else { state.transparent_render_target = false; - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); } if (render_target && render_target->clear_requested) { @@ -1121,30 +1472,28 @@ void RasterizerCanvasGLES3::canvas_begin(RID p_to_render_target, bool p_to_backb glBindTexture(GL_TEXTURE_2D, tex->tex_id); } -void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index) { +void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); if (p_texture == RID()) { - p_texture = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE); + p_texture = default_canvas_texture; } - if (state.current_tex == p_texture) { - return; //nothing to do, its the same + if (state.current_tex == p_texture && state.current_filter_mode == p_base_filter && state.current_repeat_mode == p_base_repeat) { + return; } + state.current_tex = p_texture; + state.current_filter_mode = p_base_filter; + state.current_repeat_mode = p_base_repeat; GLES3::CanvasTexture *ct = nullptr; GLES3::Texture *t = texture_storage->get_texture(p_texture); if (t) { - //regular texture - if (!t->canvas_texture) { - t->canvas_texture = memnew(GLES3::CanvasTexture); - t->canvas_texture->diffuse = p_texture; - } - + ERR_FAIL_COND(!t->canvas_texture); ct = t->canvas_texture; } else { ct = texture_storage->get_canvas_texture(p_texture); @@ -1152,7 +1501,7 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe if (!ct) { // Invalid Texture RID. - _bind_canvas_texture(default_canvas_texture, p_base_filter, p_base_repeat, r_index); + _bind_canvas_texture(default_canvas_texture, p_base_filter, p_base_repeat); return; } @@ -1165,20 +1514,12 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe GLES3::Texture *texture = texture_storage->get_texture(ct->diffuse); if (!texture) { - state.current_tex = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE); - GLES3::Texture *tex = texture_storage->get_texture(state.current_tex); - state.current_tex_ptr = tex; - ct->size_cache = Size2i(tex->width, tex->height); + GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE)); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex->tex_id); } else { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture->tex_id); - - state.current_tex = p_texture; - state.current_tex_ptr = texture; - ct->size_cache = Size2i(texture->width, texture->height); - texture->gl_set_filter(filter); texture->gl_set_repeat(repeat); } @@ -1186,45 +1527,78 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe GLES3::Texture *normal_map = texture_storage->get_texture(ct->normal_map); if (!normal_map) { - state.current_normal = RID(); - ct->use_normal_cache = false; - glActiveTexture(GL_TEXTURE0 + GLES3::Config::get_singleton()->max_texture_image_units - 6); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_NORMAL)); glBindTexture(GL_TEXTURE_2D, tex->tex_id); - } else { glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); glBindTexture(GL_TEXTURE_2D, normal_map->tex_id); - state.current_normal = ct->normal_map; - ct->use_normal_cache = true; - texture->gl_set_filter(filter); - texture->gl_set_repeat(repeat); + normal_map->gl_set_filter(filter); + normal_map->gl_set_repeat(repeat); } GLES3::Texture *specular_map = texture_storage->get_texture(ct->specular); if (!specular_map) { - state.current_specular = RID(); - ct->use_specular_cache = false; glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7); GLES3::Texture *tex = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE)); glBindTexture(GL_TEXTURE_2D, tex->tex_id); } else { glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7); glBindTexture(GL_TEXTURE_2D, specular_map->tex_id); - state.current_specular = ct->specular; - ct->use_specular_cache = true; - texture->gl_set_filter(filter); - texture->gl_set_repeat(repeat); + specular_map->gl_set_filter(filter); + specular_map->gl_set_repeat(repeat); + } +} + +void RasterizerCanvasGLES3::_prepare_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size) { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + + if (p_texture == RID()) { + p_texture = default_canvas_texture; } - if (ct->use_specular_cache) { + GLES3::CanvasTexture *ct = nullptr; + + GLES3::Texture *t = texture_storage->get_texture(p_texture); + + if (t) { + //regular texture + if (!t->canvas_texture) { + t->canvas_texture = memnew(GLES3::CanvasTexture); + t->canvas_texture->diffuse = p_texture; + } + + ct = t->canvas_texture; + } else { + ct = texture_storage->get_canvas_texture(p_texture); + } + + if (!ct) { + // Invalid Texture RID. + _prepare_canvas_texture(default_canvas_texture, p_base_filter, p_base_repeat, r_index, r_texpixel_size); + return; + } + + GLES3::Texture *texture = texture_storage->get_texture(ct->diffuse); + Size2i size_cache; + if (!texture) { + ct->diffuse = texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_WHITE); + GLES3::Texture *tex = texture_storage->get_texture(ct->diffuse); + size_cache = Size2i(tex->width, tex->height); + } else { + size_cache = Size2i(texture->width, texture->height); + } + + GLES3::Texture *normal_map = texture_storage->get_texture(ct->normal_map); + + if (ct->specular_color.a < 0.999) { state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_SPECULAR_MAP_USED; } else { state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_SPECULAR_MAP_USED; } - if (ct->use_normal_cache) { + if (normal_map) { state.instance_data_array[r_index].flags |= FLAGS_DEFAULT_NORMAL_MAP_USED; } else { state.instance_data_array[r_index].flags &= ~FLAGS_DEFAULT_NORMAL_MAP_USED; @@ -1235,11 +1609,11 @@ void RasterizerCanvasGLES3::_bind_canvas_texture(RID p_texture, RS::CanvasItemTe state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.g * 255.0, 0, 255)) << 8; state.instance_data_array[r_index].specular_shininess |= uint32_t(CLAMP(ct->specular_color.r * 255.0, 0, 255)); - state.current_pixel_size.x = 1.0 / float(ct->size_cache.x); - state.current_pixel_size.y = 1.0 / float(ct->size_cache.y); + r_texpixel_size.x = 1.0 / float(size_cache.x); + r_texpixel_size.y = 1.0 / float(size_cache.y); - state.instance_data_array[r_index].color_texture_pixel_size[0] = state.current_pixel_size.x; - state.instance_data_array[r_index].color_texture_pixel_size[1] = state.current_pixel_size.y; + state.instance_data_array[r_index].color_texture_pixel_size[0] = r_texpixel_size.x; + state.instance_data_array[r_index].color_texture_pixel_size[1] = r_texpixel_size.y; } void RasterizerCanvasGLES3::reset_canvas() { @@ -1431,20 +1805,53 @@ void RasterizerCanvasGLES3::free_polygon(PolygonID p_polygon) { // Creates a new uniform buffer and uses it right away // This expands the instance buffer continually -// In theory allocations can reach as high as number_of_draw_calls * 3 frames +// In theory allocations can reach as high as number of windows * 3 frames // because OpenGL can start rendering subsequent frames before finishing the current one void RasterizerCanvasGLES3::_allocate_instance_data_buffer() { - GLuint new_buffer; - glGenBuffers(1, &new_buffer); - glBindBuffer(GL_UNIFORM_BUFFER, new_buffer); - glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * state.max_instances_per_batch, nullptr, GL_DYNAMIC_DRAW); + GLuint new_buffers[3]; + glGenBuffers(3, new_buffers); + // Batch UBO. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[0]); + glBufferData(GL_UNIFORM_BUFFER, data.max_instance_buffer_size, nullptr, GL_STREAM_DRAW); + // Light uniform buffer. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[1]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(LightUniform) * data.max_lights_per_render, nullptr, GL_STREAM_DRAW); + // State buffer. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[2]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); + state.current_buffer = (state.current_buffer + 1); - state.canvas_instance_data_buffers.insert(state.current_buffer, new_buffer); - state.fences.insert(state.current_buffer, GLsync()); + DataBuffer db; + db.ubo = new_buffers[0]; + db.light_ubo = new_buffers[1]; + db.state_ubo = new_buffers[2]; + db.last_frame_used = RSG::rasterizer->get_frame_number(); + state.canvas_instance_data_buffers.insert(state.current_buffer, db); state.current_buffer = state.current_buffer % state.canvas_instance_data_buffers.size(); glBindBuffer(GL_UNIFORM_BUFFER, 0); } +// Batch start positions need to be aligned to the device's GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT. +// This needs to be called anytime a new batch is created. +void RasterizerCanvasGLES3::_align_instance_data_buffer(uint32_t &r_index) { + if (GLES3::Config::get_singleton()->uniform_buffer_offset_alignment > int(sizeof(InstanceData))) { + uint32_t offset = state.canvas_instance_batches[state.current_batch_index].start % GLES3::Config::get_singleton()->uniform_buffer_offset_alignment; + if (offset > 0) { + // uniform_buffer_offset_alignment can be 4, 16, 32, or 256. Our instance batches are 128 bytes. + // Accordingly, this branch is only triggered if we are 128 bytes off. + uint32_t offset_bytes = GLES3::Config::get_singleton()->uniform_buffer_offset_alignment - offset; + state.canvas_instance_batches[state.current_batch_index].start += offset_bytes; + // Offset the instance array so it stays in sync with batch start points. + // This creates gaps in the instance buffer with wasted space, but we can't help it. + r_index += offset_bytes / sizeof(InstanceData); + if (r_index > 0) { + // In this case we need to copy over the basic data. + state.instance_data_array[r_index] = state.instance_data_array[r_index - 1]; + } + } + } +} + void RasterizerCanvasGLES3::set_time(double p_time) { state.time = p_time; } @@ -1457,9 +1864,11 @@ RasterizerCanvasGLES3 *RasterizerCanvasGLES3::get_singleton() { RasterizerCanvasGLES3::RasterizerCanvasGLES3() { singleton = this; + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); + polygon_buffers.last_id = 1; // quad buffer { glGenBuffers(1, &data.canvas_quad_vertices); @@ -1583,39 +1992,77 @@ RasterizerCanvasGLES3::RasterizerCanvasGLES3() { int uniform_max_size = config->max_uniform_buffer_size; if (uniform_max_size < 65536) { - state.max_lights_per_render = 64; - state.max_instances_per_batch = 128; + data.max_lights_per_render = 64; + data.max_instances_per_batch = 128; } else { - state.max_lights_per_render = 256; - state.max_instances_per_batch = 512; + data.max_lights_per_render = 256; + data.max_instances_per_batch = 512; } - // Reserve 64 Uniform Buffers for instance data - state.canvas_instance_data_buffers.resize(64); - state.fences.resize(64); - glGenBuffers(64, state.canvas_instance_data_buffers.ptr()); - for (int i = 0; i < 64; i++) { - state.fences[i] = GLsync(); - glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_instance_data_buffers[i]); - glBufferData(GL_UNIFORM_BUFFER, sizeof(InstanceData) * state.max_instances_per_batch, nullptr, GL_DYNAMIC_DRAW); + // Reserve 3 Uniform Buffers for instance data Frame N, N+1 and N+2 + data.max_instances_per_ubo = MAX(data.max_instances_per_batch, uint32_t(GLOBAL_GET("rendering/gl_compatibility/item_buffer_size"))); + data.max_instance_buffer_size = data.max_instances_per_ubo * sizeof(InstanceData); // 16,384 instances * 128 bytes = 2,097,152 bytes = 2,048 kb + state.canvas_instance_data_buffers.resize(3); + state.canvas_instance_batches.reserve(200); + + for (int i = 0; i < 3; i++) { + GLuint new_buffers[3]; + glGenBuffers(3, new_buffers); + // Batch UBO. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[0]); + glBufferData(GL_UNIFORM_BUFFER, data.max_instance_buffer_size, nullptr, GL_STREAM_DRAW); + // Light uniform buffer. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[1]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(LightUniform) * data.max_lights_per_render, nullptr, GL_STREAM_DRAW); + // State buffer. + glBindBuffer(GL_UNIFORM_BUFFER, new_buffers[2]); + glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); + DataBuffer db; + db.ubo = new_buffers[0]; + db.light_ubo = new_buffers[1]; + db.state_ubo = new_buffers[2]; + db.last_frame_used = 0; + db.fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + state.canvas_instance_data_buffers[i] = db; } glBindBuffer(GL_UNIFORM_BUFFER, 0); - state.instance_data_array = memnew_arr(InstanceData, state.max_instances_per_batch); + state.instance_data_array = memnew_arr(InstanceData, data.max_instances_per_ubo); + state.light_uniforms = memnew_arr(LightUniform, data.max_lights_per_render); - glGenBuffers(1, &state.canvas_state_buffer); - glBindBuffer(GL_UNIFORM_BUFFER, state.canvas_state_buffer); - glBufferData(GL_UNIFORM_BUFFER, sizeof(StateBuffer), nullptr, GL_STREAM_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); + { + const uint32_t no_of_instances = data.max_instances_per_batch; + + glGenVertexArrays(1, &data.indexed_quad_array); + glBindVertexArray(data.indexed_quad_array); + glBindBuffer(GL_ARRAY_BUFFER, data.canvas_quad_vertices); + + const uint32_t num_indices = 6; + const uint32_t quad_indices[num_indices] = { 0, 2, 1, 3, 2, 0 }; + + const uint32_t total_indices = no_of_instances * num_indices; + uint32_t *indices = new uint32_t[total_indices]; + for (uint32_t i = 0; i < total_indices; i++) { + uint32_t quad = i / num_indices; + uint32_t quad_local = i % num_indices; + indices[i] = quad_indices[quad_local] + quad * num_indices; + } + + glGenBuffers(1, &data.indexed_quad_buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.indexed_quad_buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint32_t) * total_indices, indices, GL_STATIC_DRAW); + glBindVertexArray(0); + delete[] indices; + } String global_defines; global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now - global_defines += "#define MAX_LIGHTS " + itos(state.max_instances_per_batch) + "\n"; - global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(state.max_instances_per_batch) + "\n"; + global_defines += "#define MAX_LIGHTS " + itos(data.max_lights_per_render) + "\n"; + global_defines += "#define MAX_DRAW_DATA_INSTANCES " + itos(data.max_instances_per_batch) + "\n"; GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.initialize(global_defines); - state.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create(); - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(state.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD); + data.canvas_shader_default_version = GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_create(); + GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_bind_shader(data.canvas_shader_default_version, CanvasShaderGLES3::MODE_QUAD); { default_canvas_group_shader = material_storage->shader_allocate(); @@ -1642,18 +2089,40 @@ void fragment() { material_storage->material_set_shader(default_canvas_group_material, default_canvas_group_shader); } - state.current_shader_version = state.canvas_shader_default_version; + { + default_clip_children_shader = material_storage->shader_allocate(); + material_storage->shader_initialize(default_clip_children_shader); + + material_storage->shader_set_code(default_clip_children_shader, R"( +// Default clip children shader. + +shader_type canvas_item; + +void fragment() { + vec4 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0); + COLOR.rgb = c.rgb; +} +)"); + default_clip_children_material = material_storage->material_allocate(); + material_storage->material_initialize(default_clip_children_material); + + material_storage->material_set_shader(default_clip_children_material, default_clip_children_shader); + } + + default_canvas_texture = texture_storage->canvas_texture_allocate(); + texture_storage->canvas_texture_initialize(default_canvas_texture); + state.time = 0.0; } RasterizerCanvasGLES3::~RasterizerCanvasGLES3() { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); - memdelete_arr(state.instance_data_array); - - GLES3::MaterialStorage::get_singleton()->shaders.canvas_shader.version_free(state.canvas_shader_default_version); + material_storage->shaders.canvas_shader.version_free(data.canvas_shader_default_version); material_storage->material_free(default_canvas_group_material); material_storage->shader_free(default_canvas_group_shader); + material_storage->material_free(default_clip_children_material); + material_storage->shader_free(default_clip_children_shader); singleton = nullptr; glDeleteBuffers(1, &data.canvas_quad_vertices); @@ -1661,6 +2130,10 @@ RasterizerCanvasGLES3::~RasterizerCanvasGLES3() { glDeleteBuffers(1, &data.canvas_quad_vertices); glDeleteVertexArrays(1, &data.canvas_quad_array); + + GLES3::TextureStorage::get_singleton()->canvas_texture_free(default_canvas_texture); + memdelete_arr(state.instance_data_array); + memdelete_arr(state.light_uniforms); } #endif // GLES3_ENABLED diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h index 372ac00493..aee2782b62 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.h +++ b/drivers/gles3/rasterizer_canvas_gles3.h @@ -96,6 +96,33 @@ class RasterizerCanvasGLES3 : public RendererCanvasRender { DEFAULT_MAX_LIGHTS_PER_RENDER = 256, }; + /******************/ + /**** LIGHTING ****/ + /******************/ + + struct CanvasLight { + RID texture; + }; + + RID_Owner<CanvasLight> canvas_light_owner; + + struct LightUniform { + float matrix[8]; //light to texture coordinate matrix + float shadow_matrix[8]; //light to shadow coordinate matrix + float color[4]; + + uint8_t shadow_color[4]; + uint32_t flags; //index to light texture + float shadow_pixel_size; + float height; + + float position[2]; + float shadow_z_far_inv; + float shadow_y_ofs; + + float atlas_rect[4]; + }; + public: enum { BASE_UNIFORM_LOCATION = 0, @@ -125,6 +152,23 @@ public: uint32_t pad2; }; + struct PolygonBuffers { + GLuint vertex_buffer; + GLuint vertex_array; + GLuint index_buffer; + int count = 0; + bool color_disabled = false; + Color color; + }; + + struct { + HashMap<PolygonID, PolygonBuffers> polygons; + PolygonID last_id = 0; + } polygon_buffers; + + RendererCanvasRender::PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override; + void free_polygon(PolygonID p_polygon) override; + struct InstanceData { float world[6]; float color_texture_pixel_size[2]; @@ -156,42 +200,81 @@ public: GLuint canvas_quad_vertices; GLuint canvas_quad_array; + GLuint indexed_quad_buffer; + GLuint indexed_quad_array; + GLuint particle_quad_vertices; GLuint particle_quad_array; GLuint ninepatch_vertices; GLuint ninepatch_elements; + + RID canvas_shader_default_version; + + uint32_t max_lights_per_render = 256; + uint32_t max_lights_per_item = 16; + uint32_t max_instances_per_batch = 512; + uint32_t max_instances_per_ubo = 16384; + uint32_t max_instance_buffer_size = 16384 * 128; } data; + struct Batch { + // Position in the UBO measured in bytes + uint32_t start = 0; + uint32_t instance_count = 0; + + RID tex = RID(); + RS::CanvasItemTextureFilter filter = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; + RS::CanvasItemTextureRepeat repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; + + GLES3::CanvasShaderData::BlendMode blend_mode = GLES3::CanvasShaderData::BLEND_MODE_MIX; + Color blend_color = Color(1.0, 1.0, 1.0, 1.0); + + Item *clip = nullptr; + + RID material = RID(); + GLES3::CanvasMaterialData *material_data = nullptr; + CanvasShaderGLES3::ShaderVariant shader_variant = CanvasShaderGLES3::MODE_QUAD; + + const Item::Command *command = nullptr; + Item::Command::Type command_type = Item::Command::TYPE_ANIMATION_SLICE; // Can default to any type that doesn't form a batch. + uint32_t primitive_points = 0; + + bool lights_disabled = false; + }; + + // DataBuffer contains our per-frame data. I.e. the resources that are updated each frame. + // We track them and ensure that they don't get reused until at least 2 frames have passed + // to avoid the GPU stalling to wait for a resource to become available. + struct DataBuffer { + GLuint ubo = 0; + GLuint light_ubo = 0; + GLuint state_ubo = 0; + uint64_t last_frame_used = -3; + GLsync fence = GLsync(); + }; + struct State { - GLuint canvas_state_buffer; - LocalVector<GLuint> canvas_instance_data_buffers; - LocalVector<GLsync> fences; + LocalVector<DataBuffer> canvas_instance_data_buffers; + LocalVector<Batch> canvas_instance_batches; uint32_t current_buffer = 0; + uint32_t current_buffer_index = 0; + uint32_t current_batch_index = 0; InstanceData *instance_data_array = nullptr; - bool canvas_texscreen_used; - RID canvas_shader_current_version; - RID canvas_shader_default_version; + + LightUniform *light_uniforms = nullptr; + + bool using_directional_lights = false; RID current_tex = RID(); - Size2 current_pixel_size = Size2(); - RID current_normal = RID(); - RID current_specular = RID(); - GLES3::Texture *current_tex_ptr; - RID current_shader_version = RID(); - RS::PrimitiveType current_primitive = RS::PRIMITIVE_MAX; - uint32_t current_primitive_points = 0; - Item::Command::Type current_command = Item::Command::TYPE_RECT; + RS::CanvasItemTextureFilter current_filter_mode = RS::CANVAS_ITEM_TEXTURE_FILTER_MAX; + RS::CanvasItemTextureRepeat current_repeat_mode = RS::CANVAS_ITEM_TEXTURE_REPEAT_MAX; bool transparent_render_target = false; double time = 0.0; - uint32_t max_lights_per_render; - uint32_t max_lights_per_item; - uint32_t max_instances_per_batch; - RS::CanvasItemTextureFilter default_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT; RS::CanvasItemTextureRepeat default_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT; } state; @@ -201,6 +284,8 @@ public: RID default_canvas_texture; RID default_canvas_group_material; RID default_canvas_group_shader; + RID default_clip_children_material; + RID default_clip_children_shader; typedef void Texture; @@ -229,31 +314,18 @@ public: bool free(RID p_rid) override; void update() override; - void _bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index); - - struct PolygonBuffers { - GLuint vertex_buffer; - GLuint vertex_array; - GLuint index_buffer; - int count; - bool color_disabled = false; - Color color; - }; - - struct { - HashMap<PolygonID, PolygonBuffers> polygons; - PolygonID last_id; - } polygon_buffers; - - RendererCanvasRender::PolygonID request_polygon(const Vector<int> &p_indices, const Vector<Point2> &p_points, const Vector<Color> &p_colors, const Vector<Point2> &p_uvs = Vector<Point2>(), const Vector<int> &p_bones = Vector<int>(), const Vector<float> &p_weights = Vector<float>()) override; - void free_polygon(PolygonID p_polygon) override; + void _bind_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat); + void _prepare_canvas_texture(RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, uint32_t &r_index, Size2 &r_texpixel_size); void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used) override; - void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool p_to_backbuffer = false); - void _render_item(RID p_render_target, const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, Light *p_lights, uint32_t &r_index, GLES3::CanvasShaderData::BlendMode p_blend_mode, GLES3::CanvasShaderData::BlendMode &r_last_blend_mode, Color &r_last_blend_color); - void _render_batch(uint32_t &p_max_index); - void _bind_instance_data_buffer(uint32_t p_max_index); + void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, uint32_t &r_last_index, bool p_to_backbuffer = false); + void _record_item_commands(const Item *p_item, const Transform2D &p_canvas_transform_inverse, Item *¤t_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch); + void _render_batch(Light *p_lights, uint32_t p_index); + void _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization); + void _new_batch(bool &r_batch_broken, uint32_t &r_index); + void _add_to_batch(uint32_t &r_index, bool &r_batch_broken); void _allocate_instance_data_buffer(); + void _align_instance_data_buffer(uint32_t &r_index); void set_time(double p_time); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index 3575837794..ea97dff522 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -100,9 +100,9 @@ void RasterizerGLES3::begin_frame(double frame_step) { canvas->set_time(time_total); scene->set_time(time_total, frame_step); - GLES3::Utilities *utilities = GLES3::Utilities::get_singleton(); - utilities->info.render_final = utilities->info.render; - utilities->info.render.reset(); + GLES3::Utilities *utils = GLES3::Utilities::get_singleton(); + utils->info.render_final = utils->info.render; + utils->info.render.reset(); //scene->iteration(); } @@ -274,21 +274,32 @@ RasterizerGLES3::~RasterizerGLES3() { void RasterizerGLES3::prepare_for_blitting_render_targets() { } -void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect) { - GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); +void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer) { + GLES3::RenderTarget *rt = GLES3::TextureStorage::get_singleton()->get_render_target(p_render_target); - GLES3::RenderTarget *rt = texture_storage->get_render_target(p_render_target); ERR_FAIL_COND(!rt); - if (rt->external.fbo != 0) { - glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->external.fbo); + GLuint read_fbo = 0; + if (rt->view_count > 1) { + glGenFramebuffers(1, &read_fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, read_fbo); + glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, p_layer); } else { glBindFramebuffer(GL_READ_FRAMEBUFFER, rt->fbo); } + glReadBuffer(GL_COLOR_ATTACHMENT0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); // Flip content upside down to correct for coordinates. - glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, 0, p_screen_rect.size.y, p_screen_rect.size.x, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST); + Vector2i screen_rect_end = p_screen_rect.get_end(); + glBlitFramebuffer(0, 0, rt->size.x, rt->size.y, + p_screen_rect.position.x, screen_rect_end.y, screen_rect_end.x, p_screen_rect.position.y, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + + if (read_fbo != 0) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &read_fbo); + } } // is this p_screen useless in a multi window environment? @@ -299,7 +310,7 @@ void RasterizerGLES3::blit_render_targets_to_screen(DisplayServer::WindowID p_sc RID rid_rt = blit.render_target; Rect2 dst_rect = blit.dst_rect; - _blit_render_target_to_screen(rid_rt, p_screen, dst_rect); + _blit_render_target_to_screen(rid_rt, p_screen, dst_rect, blit.multi_view.use_layer ? blit.multi_view.layer : 0); } } diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index 97543af0d5..d7d26685b4 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -68,7 +68,7 @@ protected: RasterizerCanvasGLES3 *canvas = nullptr; RasterizerSceneGLES3 *scene = nullptr; - void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect); + void _blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer); public: RendererUtilities *get_utilities() { return utilities; } @@ -103,8 +103,9 @@ public: low_end = true; } - uint64_t get_frame_number() const { return frame; } - double get_frame_delta_time() const { return delta; } + _ALWAYS_INLINE_ uint64_t get_frame_number() const { return frame; } + _ALWAYS_INLINE_ double get_frame_delta_time() const { return delta; } + _ALWAYS_INLINE_ double get_total_time() const { return time_total; } RasterizerGLES3(); ~RasterizerGLES3(); diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 1952efff0e..6e9d4e5963 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -34,7 +34,6 @@ #include "servers/rendering/rendering_server_default.h" #include "servers/rendering/rendering_server_globals.h" #include "storage/config.h" -#include "storage/light_storage.h" #include "storage/mesh_storage.h" #include "storage/texture_storage.h" @@ -70,7 +69,7 @@ void RasterizerSceneGLES3::GeometryInstanceGLES3::pair_light_instances(const RID spot_lights.clear(); for (uint32_t i = 0; i < p_light_instance_count; i++) { - RS::LightType type = RasterizerSceneGLES3::get_singleton()->light_instance_get_type(p_light_instances[i]); + RS::LightType type = GLES3::LightStorage::get_singleton()->light_instance_get_type(p_light_instances[i]); switch (type) { case RS::LIGHT_OMNI: { if (omni_light_count < (uint32_t)config->max_lights_per_object) { @@ -399,32 +398,6 @@ void RasterizerSceneGLES3::_geometry_instance_update(RenderGeometryInstance *p_g ginstance->dirty_list_element.remove_from_list(); } -/* SHADOW ATLAS API */ - -RID RasterizerSceneGLES3::shadow_atlas_create() { - return RID(); -} - -void RasterizerSceneGLES3::shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits) { -} - -void RasterizerSceneGLES3::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) { -} - -bool RasterizerSceneGLES3::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) { - return false; -} - -void RasterizerSceneGLES3::directional_shadow_atlas_set_size(int p_size, bool p_16_bits) { -} - -int RasterizerSceneGLES3::get_directional_light_shadow_size(RID p_light_intance) { - return 0; -} - -void RasterizerSceneGLES3::set_directional_shadow_count(int p_count) { -} - /* SKY API */ void RasterizerSceneGLES3::_free_sky_data(Sky *p_sky) { @@ -625,7 +598,7 @@ void RasterizerSceneGLES3::_setup_sky(const RenderDataGLES3 *p_render_data, cons if (shader_data->uses_light) { sky_globals.directional_light_count = 0; for (int i = 0; i < (int)p_lights.size(); i++) { - LightInstance *li = light_instance_owner.get_or_null(p_lights[i]); + GLES3::LightInstance *li = GLES3::LightStorage::get_singleton()->get_light_instance(p_lights[i]); if (!li) { continue; } @@ -737,7 +710,6 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, RS::EnvironmentBG background = environment_get_background(p_env); if (sky) { - ERR_FAIL_COND(!sky); sky_material = sky->material; if (sky_material.is_valid()) { @@ -781,7 +753,7 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_bind_shader(shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::ORIENTATION, sky_transform, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, camera.matrix[2][0], camera.matrix[0][0], camera.matrix[2][1], camera.matrix[1][1], shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, camera.columns[2][0], camera.columns[0][0], camera.columns[2][1], camera.columns[1][1], shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND); @@ -882,7 +854,7 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); - GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, cm.matrix[2][0], cm.matrix[0][0], cm.matrix[2][1], cm.matrix[1][1], shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, cm.columns[2][0], cm.columns[0][0], cm.columns[2][1], cm.columns[1][1], shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); GLES3::MaterialStorage::get_singleton()->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); glBindVertexArray(sky_globals.screen_triangle_array); @@ -1085,38 +1057,6 @@ void RasterizerSceneGLES3::positional_soft_shadow_filter_set_quality(RS::ShadowQ void RasterizerSceneGLES3::directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) { } -RID RasterizerSceneGLES3::light_instance_create(RID p_light) { - RID li = light_instance_owner.make_rid(LightInstance()); - - LightInstance *light_instance = light_instance_owner.get_or_null(li); - - light_instance->self = li; - light_instance->light = p_light; - light_instance->light_type = RSG::light_storage->light_get_type(p_light); - - return li; -} - -void RasterizerSceneGLES3::light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) { - LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); - ERR_FAIL_COND(!light_instance); - - light_instance->transform = p_transform; -} - -void RasterizerSceneGLES3::light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) { - LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); - ERR_FAIL_COND(!light_instance); - - light_instance->aabb = p_aabb; -} - -void RasterizerSceneGLES3::light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale, float p_range_begin, const Vector2 &p_uv_scale) { -} - -void RasterizerSceneGLES3::light_instance_mark_visible(RID p_light_instance) { -} - RID RasterizerSceneGLES3::fog_volume_instance_create(RID p_fog_volume) { return RID(); } @@ -1135,57 +1075,6 @@ Vector3 RasterizerSceneGLES3::fog_volume_instance_get_position(RID p_fog_volume_ return Vector3(); } -RID RasterizerSceneGLES3::reflection_atlas_create() { - return RID(); -} - -int RasterizerSceneGLES3::reflection_atlas_get_size(RID p_ref_atlas) const { - return 0; -} - -void RasterizerSceneGLES3::reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) { -} - -RID RasterizerSceneGLES3::reflection_probe_instance_create(RID p_probe) { - return RID(); -} - -void RasterizerSceneGLES3::reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) { -} - -void RasterizerSceneGLES3::reflection_probe_release_atlas_index(RID p_instance) { -} - -bool RasterizerSceneGLES3::reflection_probe_instance_needs_redraw(RID p_instance) { - return false; -} - -bool RasterizerSceneGLES3::reflection_probe_instance_has_reflection(RID p_instance) { - return false; -} - -bool RasterizerSceneGLES3::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) { - return false; -} - -bool RasterizerSceneGLES3::reflection_probe_instance_postprocess_step(RID p_instance) { - return true; -} - -RID RasterizerSceneGLES3::decal_instance_create(RID p_decal) { - return RID(); -} - -void RasterizerSceneGLES3::decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) { -} - -RID RasterizerSceneGLES3::lightmap_instance_create(RID p_lightmap) { - return RID(); -} - -void RasterizerSceneGLES3::lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) { -} - RID RasterizerSceneGLES3::voxel_gi_instance_create(RID p_voxel_gi) { return RID(); } @@ -1258,13 +1147,13 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const if (inst->omni_light_count) { inst->omni_light_gl_cache.resize(inst->omni_light_count); for (uint32_t j = 0; j < inst->omni_light_count; j++) { - inst->omni_light_gl_cache[j] = light_instance_get_gl_id(inst->omni_lights[j]); + inst->omni_light_gl_cache[j] = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(inst->omni_lights[j]); } } if (inst->spot_light_count) { inst->spot_light_gl_cache.resize(inst->spot_light_count); for (uint32_t j = 0; j < inst->spot_light_count; j++) { - inst->spot_light_gl_cache[j] = light_instance_get_gl_id(inst->spot_lights[j]); + inst->spot_light_gl_cache[j] = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(inst->spot_lights[j]); } } } @@ -1277,12 +1166,13 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const // LOD if (p_render_data->screen_mesh_lod_threshold > 0.0 && mesh_storage->mesh_surface_has_lod(surf->surface)) { - //lod - Vector3 lod_support_min = inst->transformed_aabb.get_support(-p_render_data->lod_camera_plane.normal); - Vector3 lod_support_max = inst->transformed_aabb.get_support(p_render_data->lod_camera_plane.normal); + // Get the LOD support points on the mesh AABB. + Vector3 lod_support_min = inst->transformed_aabb.get_support(p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); + Vector3 lod_support_max = inst->transformed_aabb.get_support(-p_render_data->cam_transform.basis.get_column(Vector3::AXIS_Z)); - float distance_min = p_render_data->lod_camera_plane.distance_to(lod_support_min); - float distance_max = p_render_data->lod_camera_plane.distance_to(lod_support_max); + // Get the distances to those points on the AABB from the camera origin. + float distance_min = (float)p_render_data->cam_transform.origin.distance_to(lod_support_min); + float distance_max = (float)p_render_data->cam_transform.origin.distance_to(lod_support_max); float distance = 0.0; @@ -1299,8 +1189,8 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const distance = 1.0; } - uint32_t indices; - surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, &indices); + uint32_t indices = 0; + surf->lod_index = mesh_storage->mesh_surface_get_lod(surf->surface, inst->lod_model_scale * inst->lod_bias, distance * p_render_data->lod_distance_multiplier, p_render_data->screen_mesh_lod_threshold, indices); /* if (p_render_data->render_info) { indices = _indices_to_primitives(surf->primitive, indices); @@ -1383,6 +1273,19 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da GLES3::MaterialStorage::store_transform(p_render_data->cam_transform, scene_state.ubo.inv_view_matrix); GLES3::MaterialStorage::store_transform(p_render_data->inv_cam_transform, scene_state.ubo.view_matrix); + if (p_render_data->view_count > 1) { + for (uint32_t v = 0; v < p_render_data->view_count; v++) { + projection = correction * p_render_data->view_projection[v]; + GLES3::MaterialStorage::store_camera(projection, scene_state.multiview_ubo.projection_matrix_view[v]); + GLES3::MaterialStorage::store_camera(projection.inverse(), scene_state.multiview_ubo.inv_projection_matrix_view[v]); + + scene_state.multiview_ubo.eye_offset[v][0] = p_render_data->view_eye_offset[v].x; + scene_state.multiview_ubo.eye_offset[v][1] = p_render_data->view_eye_offset[v].y; + scene_state.multiview_ubo.eye_offset[v][2] = p_render_data->view_eye_offset[v].z; + scene_state.multiview_ubo.eye_offset[v][3] = 0.0; + } + } + scene_state.ubo.directional_light_count = p_render_data->directional_light_count; scene_state.ubo.z_far = p_render_data->z_far; @@ -1484,6 +1387,15 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_DATA_UNIFORM_LOCATION, scene_state.ubo_buffer); glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::UBO), &scene_state.ubo, GL_STREAM_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); + + if (p_render_data->view_count > 1) { + if (scene_state.multiview_buffer == 0) { + glGenBuffers(1, &scene_state.multiview_buffer); + } + glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(SceneState::MultiviewUBO), &scene_state.multiview_ubo, GL_STREAM_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } } // Puts lights into Uniform Buffers. Needs to be called before _fill_list as this caches the index of each light in the Uniform Buffer @@ -1502,7 +1414,7 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b int num_lights = lights.size(); for (int i = 0; i < num_lights; i++) { - LightInstance *li = light_instance_owner.get_or_null(lights[i]); + GLES3::LightInstance *li = GLES3::LightStorage::get_singleton()->get_light_instance(lights[i]); if (!li) { continue; } @@ -1607,12 +1519,12 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b } if (r_omni_light_count) { - SortArray<InstanceSort<LightInstance>> sorter; + SortArray<InstanceSort<GLES3::LightInstance>> sorter; sorter.sort(scene_state.omni_light_sort, r_omni_light_count); } if (r_spot_light_count) { - SortArray<InstanceSort<LightInstance>> sorter; + SortArray<InstanceSort<GLES3::LightInstance>> sorter; sorter.sort(scene_state.spot_light_sort, r_spot_light_count); } @@ -1620,7 +1532,7 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b uint32_t index = (i < r_omni_light_count) ? i : i - (r_omni_light_count); LightData &light_data = (i < r_omni_light_count) ? scene_state.omni_lights[index] : scene_state.spot_lights[index]; RS::LightType type = (i < r_omni_light_count) ? RS::LIGHT_OMNI : RS::LIGHT_SPOT; - LightInstance *li = (i < r_omni_light_count) ? scene_state.omni_light_sort[index].instance : scene_state.spot_light_sort[index].instance; + GLES3::LightInstance *li = (i < r_omni_light_count) ? scene_state.omni_light_sort[index].instance : scene_state.spot_light_sort[index].instance; RID base = li->light; Transform3D light_transform = li->transform; @@ -1719,7 +1631,7 @@ void RasterizerSceneGLES3::_setup_lights(const RenderDataGLES3 *p_render_data, b glBindBuffer(GL_UNIFORM_BUFFER, 0); } -void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RendererScene::RenderInfo *r_render_info) { +void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RenderingMethod::RenderInfo *r_render_info) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLES3::Config *config = GLES3::Config::get_singleton(); RENDER_TIMESTAMP("Setup 3D Scene"); @@ -1761,7 +1673,6 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_ // this should be the same for all cameras.. render_data.lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier(); - render_data.lod_camera_plane = Plane(-p_camera_data->main_transform.basis.get_column(Vector3::AXIS_Z), p_camera_data->main_transform.get_origin()); if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) { render_data.screen_mesh_lod_threshold = 0.0; @@ -2027,8 +1938,14 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, GLES3::SceneShaderData *prev_shader = nullptr; GeometryInstanceGLES3 *prev_inst = nullptr; SceneShaderGLES3::ShaderVariant prev_variant = SceneShaderGLES3::ShaderVariant::MODE_COLOR; + SceneShaderGLES3::ShaderVariant shader_variant = SceneShaderGLES3::MODE_COLOR; // Assigned to silence wrong -Wmaybe-initialized + + // @todo Get this from p_params->spec_constant_base_flags instead of hardcoding it. + uint32_t base_spec_constants = 0; - SceneShaderGLES3::ShaderVariant shader_variant = SceneShaderGLES3::MODE_COLOR; // Assigned to silence wrong -Wmaybe-initialized. + if (p_render_data->view_count > 1) { + base_spec_constants |= 1 << SPEC_CONSTANT_USE_MULTIVIEW; + } switch (p_pass_mode) { case PASS_MODE_COLOR: @@ -2043,7 +1960,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } break; } - if (p_pass_mode == PASS_MODE_COLOR || p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { + if constexpr (p_pass_mode == PASS_MODE_COLOR || p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 2); GLuint texture_to_bind = texture_storage->get_texture(texture_storage->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_CUBEMAP_BLACK))->tex_id; if (p_render_data->environment.is_valid()) { @@ -2068,13 +1985,11 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, continue; } - //uint32_t base_spec_constants = p_params->spec_constant_base_flags; - GLES3::SceneShaderData *shader; GLES3::SceneMaterialData *material_data; void *mesh_surface; - if (p_pass_mode == PASS_MODE_SHADOW) { + if constexpr (p_pass_mode == PASS_MODE_SHADOW) { shader = surf->shader_shadow; material_data = surf->material_shadow; mesh_surface = surf->surface_shadow; @@ -2088,7 +2003,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, continue; } - if (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { + if constexpr (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { if (scene_state.current_depth_test != shader->depth_test) { if (shader->depth_test == GLES3::SceneShaderData::DEPTH_TEST_DISABLED) { glDisable(GL_DEPTH_TEST); @@ -2115,9 +2030,9 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, scene_state.current_depth_draw = shader->depth_draw; } - if (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT || p_pass_mode == PASS_MODE_COLOR_ADDITIVE) { + if constexpr (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT || p_pass_mode == PASS_MODE_COLOR_ADDITIVE) { GLES3::SceneShaderData::BlendMode desired_blend_mode; - if (p_pass_mode == PASS_MODE_COLOR_ADDITIVE) { + if constexpr (p_pass_mode == PASS_MODE_COLOR_ADDITIVE) { desired_blend_mode = GLES3::SceneShaderData::BLEND_MODE_ADD; } else { desired_blend_mode = shader->blend_mode; @@ -2239,15 +2154,15 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } if (prev_shader != shader || prev_variant != instance_variant) { - material_storage->shaders.scene_shader.version_bind_shader(shader->version, instance_variant); + material_storage->shaders.scene_shader.version_bind_shader(shader->version, instance_variant, base_spec_constants); float opaque_prepass_threshold = 0.0; - if (p_pass_mode == PASS_MODE_DEPTH) { + if constexpr (p_pass_mode == PASS_MODE_DEPTH) { opaque_prepass_threshold = 0.99; - } else if (p_pass_mode == PASS_MODE_SHADOW) { + } else if constexpr (p_pass_mode == PASS_MODE_SHADOW) { opaque_prepass_threshold = 0.1; } - material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OPAQUE_PREPASS_THRESHOLD, opaque_prepass_threshold, shader->version, instance_variant); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OPAQUE_PREPASS_THRESHOLD, opaque_prepass_threshold, shader->version, instance_variant, base_spec_constants); prev_shader = shader; prev_variant = instance_variant; @@ -2255,21 +2170,21 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if (prev_inst != inst || prev_shader != shader || prev_variant != instance_variant) { // Rebind the light indices. - material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OMNI_LIGHT_COUNT, inst->omni_light_count, shader->version, instance_variant); - material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_count, shader->version, instance_variant); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::OMNI_LIGHT_COUNT, inst->omni_light_count, shader->version, instance_variant, base_spec_constants); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::SPOT_LIGHT_COUNT, inst->spot_light_count, shader->version, instance_variant, base_spec_constants); if (inst->omni_light_count) { - glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::OMNI_LIGHT_INDICES, shader->version, instance_variant), inst->omni_light_count, inst->omni_light_gl_cache.ptr()); + glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::OMNI_LIGHT_INDICES, shader->version, instance_variant, base_spec_constants), inst->omni_light_count, inst->omni_light_gl_cache.ptr()); } if (inst->spot_light_count) { - glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, instance_variant), inst->spot_light_count, inst->spot_light_gl_cache.ptr()); + glUniform1uiv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::SPOT_LIGHT_INDICES, shader->version, instance_variant, base_spec_constants), inst->spot_light_count, inst->spot_light_gl_cache.ptr()); } prev_inst = inst; } - material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant, base_spec_constants); if (inst->instance_count > 0) { // Using MultiMesh. // Bind instance buffers. @@ -2391,10 +2306,8 @@ bool RasterizerSceneGLES3::free(RID p_rid) { ERR_FAIL_COND_V(!sky, false); _free_sky_data(sky); sky_owner.free(p_rid); - } else if (light_instance_owner.owns(p_rid)) { - LightInstance *light_instance = light_instance_owner.get_or_null(p_rid); - ERR_FAIL_COND_V(!light_instance, false); - light_instance_owner.free(p_rid); + } else if (GLES3::LightStorage::get_singleton()->owns_light_instance(p_rid)) { + GLES3::LightStorage::get_singleton()->light_instance_free(p_rid); } else if (RSG::camera_attributes->owns_camera_attributes(p_rid)) { //not much to delete, just free it RSG::camera_attributes->camera_attributes_free(p_rid); @@ -2434,13 +2347,13 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() { uint32_t light_buffer_size = config->max_renderable_lights * sizeof(LightData); scene_state.omni_lights = memnew_arr(LightData, config->max_renderable_lights); - scene_state.omni_light_sort = memnew_arr(InstanceSort<LightInstance>, config->max_renderable_lights); + scene_state.omni_light_sort = memnew_arr(InstanceSort<GLES3::LightInstance>, config->max_renderable_lights); glGenBuffers(1, &scene_state.omni_light_buffer); glBindBuffer(GL_UNIFORM_BUFFER, scene_state.omni_light_buffer); glBufferData(GL_UNIFORM_BUFFER, light_buffer_size, nullptr, GL_STREAM_DRAW); scene_state.spot_lights = memnew_arr(LightData, config->max_renderable_lights); - scene_state.spot_light_sort = memnew_arr(InstanceSort<LightInstance>, config->max_renderable_lights); + scene_state.spot_light_sort = memnew_arr(InstanceSort<GLES3::LightInstance>, config->max_renderable_lights); glGenBuffers(1, &scene_state.spot_light_buffer); glBindBuffer(GL_UNIFORM_BUFFER, scene_state.spot_light_buffer); glBufferData(GL_UNIFORM_BUFFER, light_buffer_size, nullptr, GL_STREAM_DRAW); @@ -2481,7 +2394,7 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() { scene_globals.default_shader = material_storage->shader_allocate(); material_storage->shader_initialize(scene_globals.default_shader); material_storage->shader_set_code(scene_globals.default_shader, R"( -// Default 3D material shader (clustered). +// Default 3D material shader. shader_type spatial; diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index d264284ef7..3a759425e2 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -44,6 +44,7 @@ #include "shader_gles3.h" #include "shaders/cubemap_filter.glsl.gen.h" #include "shaders/sky.glsl.gen.h" +#include "storage/light_storage.h" #include "storage/material_storage.h" #include "storage/render_scene_buffers_gles3.h" #include "storage/utilities.h" @@ -73,6 +74,7 @@ enum SceneUniformLocation { SCENE_OMNILIGHT_UNIFORM_LOCATION, SCENE_SPOTLIGHT_UNIFORM_LOCATION, SCENE_DIRECTIONAL_LIGHT_UNIFORM_LOCATION, + SCENE_MULTIVIEW_UNIFORM_LOCATION, }; enum SkyUniformLocation { @@ -89,6 +91,8 @@ enum { SPEC_CONSTANT_DISABLE_OMNI_LIGHTS = 2, SPEC_CONSTANT_DISABLE_SPOT_LIGHTS = 3, SPEC_CONSTANT_DISABLE_FOG = 4, + SPEC_CONSTANT_USE_RADIANCE_MAP = 5, + SPEC_CONSTANT_USE_MULTIVIEW = 6, }; struct RenderDataGLES3 { @@ -117,14 +121,13 @@ struct RenderDataGLES3 { int reflection_probe_pass = 0; float lod_distance_multiplier = 0.0; - Plane lod_camera_plane = Plane(); float screen_mesh_lod_threshold = 0.0; uint32_t directional_light_count = 0; uint32_t spot_light_count = 0; uint32_t omni_light_count = 0; - RendererScene::RenderInfo *render_info = nullptr; + RenderingMethod::RenderInfo *render_info = nullptr; }; class RasterizerCanvasGLES3; @@ -183,34 +186,6 @@ private: }; static_assert(sizeof(DirectionalLightData) % 16 == 0, "DirectionalLightData size must be a multiple of 16 bytes"); - struct LightInstance { - RS::LightType light_type = RS::LIGHT_DIRECTIONAL; - - AABB aabb; - RID self; - RID light; - Transform3D transform; - - Vector3 light_vector; - Vector3 spot_vector; - float linear_att = 0.0; - - uint64_t shadow_pass = 0; - uint64_t last_scene_pass = 0; - uint64_t last_scene_shadow_pass = 0; - uint64_t last_pass = 0; - uint32_t cull_mask = 0; - uint32_t light_directional_index = 0; - - Rect2 directional_rect; - - uint32_t gl_id = -1; - - LightInstance() {} - }; - - mutable RID_Owner<LightInstance> light_instance_owner; - class GeometryInstanceGLES3; // Cached data for drawing surfaces @@ -371,6 +346,13 @@ private: }; static_assert(sizeof(UBO) % 16 == 0, "Scene UBO size must be a multiple of 16 bytes"); + struct MultiviewUBO { + float projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16]; + float inv_projection_matrix_view[RendererSceneRender::MAX_RENDER_VIEWS][16]; + float eye_offset[RendererSceneRender::MAX_RENDER_VIEWS][4]; + }; + static_assert(sizeof(MultiviewUBO) % 16 == 0, "Multiview UBO size must be a multiple of 16 bytes"); + struct TonemapUBO { float exposure = 1.0; float white = 1.0; @@ -381,6 +363,8 @@ private: UBO ubo; GLuint ubo_buffer = 0; + MultiviewUBO multiview_ubo; + GLuint multiview_buffer = 0; GLuint tonemap_buffer = 0; bool used_depth_prepass = false; @@ -398,8 +382,8 @@ private: LightData *omni_lights = nullptr; LightData *spot_lights = nullptr; - InstanceSort<LightInstance> *omni_light_sort; - InstanceSort<LightInstance> *spot_light_sort; + InstanceSort<GLES3::LightInstance> *omni_light_sort; + InstanceSort<GLES3::LightInstance> *spot_light_sort; GLuint omni_light_buffer = 0; GLuint spot_light_buffer = 0; uint32_t omni_light_count = 0; @@ -605,17 +589,6 @@ public: uint32_t geometry_instance_get_pair_mask() override; - /* SHADOW ATLAS API */ - - RID shadow_atlas_create() override; - void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = true) override; - void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override; - bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) override; - - void directional_shadow_atlas_set_size(int p_size, bool p_16_bits = true) override; - int get_directional_light_shadow_size(RID p_light_intance) override; - void set_directional_shadow_count(int p_count) override; - /* SDFGI UPDATE */ void sdfgi_update(const Ref<RenderSceneBuffers> &p_render_buffers, RID p_environment, const Vector3 &p_world_position) override {} @@ -666,45 +639,12 @@ public: void positional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override; void directional_soft_shadow_filter_set_quality(RS::ShadowQuality p_quality) override; - RID light_instance_create(RID p_light) override; - void light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) override; - void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override; - void light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override; - void light_instance_mark_visible(RID p_light_instance) override; - - _FORCE_INLINE_ RS::LightType light_instance_get_type(RID p_light_instance) { - LightInstance *li = light_instance_owner.get_or_null(p_light_instance); - return li->light_type; - } - _FORCE_INLINE_ uint32_t light_instance_get_gl_id(RID p_light_instance) { - LightInstance *li = light_instance_owner.get_or_null(p_light_instance); - return li->gl_id; - } - RID fog_volume_instance_create(RID p_fog_volume) override; void fog_volume_instance_set_transform(RID p_fog_volume_instance, const Transform3D &p_transform) override; void fog_volume_instance_set_active(RID p_fog_volume_instance, bool p_active) override; RID fog_volume_instance_get_volume(RID p_fog_volume_instance) const override; Vector3 fog_volume_instance_get_position(RID p_fog_volume_instance) const override; - RID reflection_atlas_create() override; - int reflection_atlas_get_size(RID p_ref_atlas) const override; - void reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) override; - - RID reflection_probe_instance_create(RID p_probe) override; - void reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) override; - void reflection_probe_release_atlas_index(RID p_instance) override; - bool reflection_probe_instance_needs_redraw(RID p_instance) override; - bool reflection_probe_instance_has_reflection(RID p_instance) override; - bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override; - bool reflection_probe_instance_postprocess_step(RID p_instance) override; - - RID decal_instance_create(RID p_decal) override; - void decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) override; - - RID lightmap_instance_create(RID p_lightmap) override; - void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override; - RID voxel_gi_instance_create(RID p_voxel_gi) override; void voxel_gi_instance_set_transform_to_data(RID p_probe, const Transform3D &p_xform) override; bool voxel_gi_needs_update(RID p_probe) const override; @@ -712,7 +652,7 @@ public: void voxel_gi_set_quality(RS::VoxelGIQuality) override; - void render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RendererScene::RenderInfo *r_render_info = nullptr) override; + void render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) override; void render_material(const Transform3D &p_cam_transform, const Projection &p_cam_projection, bool p_cam_orthogonal, const PagedArray<RenderGeometryInstance *> &p_instances, RID p_framebuffer, const Rect2i &p_region) override; void render_particle_collider_heightfield(RID p_collider, const Transform3D &p_transform, const PagedArray<RenderGeometryInstance *> &p_instances) override; diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 033f10dbc5..2ff7f72180 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -142,7 +142,7 @@ RID ShaderGLES3::version_create() { return version_owner.make_rid(version); } -void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization) { +void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization) { #ifdef GLES_OVER_GL builder.append("#version 330\n"); builder.append("#define USE_GLES_OVER_GL\n"); @@ -171,6 +171,24 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant } builder.append("\n"); //make sure defines begin at newline + // Insert multiview extension loading, because it needs to appear before + // any non-preprocessor code (like the "precision highp..." lines below). + builder.append("#ifdef USE_MULTIVIEW\n"); + builder.append("#if defined(GL_OVR_multiview2)\n"); + builder.append("#extension GL_OVR_multiview2 : require\n"); + builder.append("#elif defined(GL_OVR_multiview)\n"); + builder.append("#extension GL_OVR_multiview : require\n"); + builder.append("#endif\n"); + if (p_stage_type == StageType::STAGE_TYPE_VERTEX) { + builder.append("layout(num_views=2) in;\n"); + } + builder.append("#define ViewIndex gl_ViewID_OVR\n"); + builder.append("#define MAX_VIEWS 2\n"); + builder.append("#else\n"); + builder.append("#define ViewIndex 0\n"); + builder.append("#define MAX_VIEWS 1\n"); + builder.append("#endif\n"); + // Default to highp precision unless specified otherwise. builder.append("precision highp float;\n"); builder.append("precision highp int;\n"); @@ -180,8 +198,9 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant builder.append("precision highp sampler2DArray;\n"); #endif - for (uint32_t i = 0; i < p_template.chunks.size(); i++) { - const StageTemplate::Chunk &chunk = p_template.chunks[i]; + const StageTemplate &stage_template = stage_templates[p_stage_type]; + for (uint32_t i = 0; i < stage_template.chunks.size(); i++) { + const StageTemplate::Chunk &chunk = stage_template.chunks[i]; switch (chunk.type) { case StageTemplate::Chunk::TYPE_MATERIAL_UNIFORMS: { builder.append(p_version->uniforms.get_data()); //uniforms (same for vertex and fragment) @@ -224,7 +243,7 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_ //vertex stage { StringBuilder builder; - _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_VERTEX], p_specialization); + _build_variant_code(builder, p_variant, p_version, STAGE_TYPE_VERTEX, p_specialization); spec.vert_id = glCreateShader(GL_VERTEX_SHADER); String builder_string = builder.as_string(); @@ -272,7 +291,7 @@ void ShaderGLES3::_compile_specialization(Version::Specialization &spec, uint32_ //fragment stage { StringBuilder builder; - _build_variant_code(builder, p_variant, p_version, stage_templates[STAGE_TYPE_FRAGMENT], p_specialization); + _build_variant_code(builder, p_variant, p_version, STAGE_TYPE_FRAGMENT, p_specialization); spec.frag_id = glCreateShader(GL_FRAGMENT_SHADER); String builder_string = builder.as_string(); @@ -413,7 +432,7 @@ RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_ver { StringBuilder builder; - _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_VERTEX], specialization_default_mask); + _build_variant_code(builder, i, version, STAGE_TYPE_VERTEX, specialization_default_mask); RS::ShaderNativeSourceCode::Version::Stage stage; stage.name = "vertex"; @@ -425,7 +444,7 @@ RS::ShaderNativeSourceCode ShaderGLES3::version_get_native_source_code(RID p_ver //fragment stage { StringBuilder builder; - _build_variant_code(builder, i, version, stage_templates[STAGE_TYPE_FRAGMENT], specialization_default_mask); + _build_variant_code(builder, i, version, STAGE_TYPE_FRAGMENT, specialization_default_mask); RS::ShaderNativeSourceCode::Version::Stage stage; stage.name = "fragment"; diff --git a/drivers/gles3/shader_gles3.h b/drivers/gles3/shader_gles3.h index 2b72549b5b..760b5e5ddb 100644 --- a/drivers/gles3/shader_gles3.h +++ b/drivers/gles3/shader_gles3.h @@ -153,7 +153,7 @@ private: StageTemplate stage_templates[STAGE_TYPE_MAX]; - void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, const StageTemplate &p_template, uint64_t p_specialization); + void _build_variant_code(StringBuilder &p_builder, uint32_t p_variant, const Version *p_version, StageType p_stage_type, uint64_t p_specialization); void _add_stage(const char *p_code, StageType p_stage_type); diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index 7334100575..23db41802e 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -60,20 +60,18 @@ out vec2 pixel_size_interp; void main() { vec4 instance_custom = vec4(0.0); - draw_data_instance = gl_InstanceID; -#ifdef USE_PRIMITIVE - //weird bug, - //this works +#ifdef USE_PRIMITIVE + draw_data_instance = gl_InstanceID; vec2 vertex; vec2 uv; vec4 color; - if (gl_VertexID == 0) { + if (gl_VertexID % 3 == 0) { vertex = draw_data[draw_data_instance].point_a; uv = draw_data[draw_data_instance].uv_a; color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_a_rg), unpackHalf2x16(draw_data[draw_data_instance].color_a_ba)); - } else if (gl_VertexID == 1) { + } else if (gl_VertexID % 3 == 1) { vertex = draw_data[draw_data_instance].point_b; uv = draw_data[draw_data_instance].uv_b; color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_b_rg), unpackHalf2x16(draw_data[draw_data_instance].color_b_ba)); @@ -86,6 +84,7 @@ void main() { vec4 bone_weights = vec4(0.0); #elif defined(USE_ATTRIBUTES) + draw_data_instance = gl_InstanceID; #ifdef USE_INSTANCING draw_data_instance = 0; #endif @@ -103,9 +102,9 @@ void main() { #endif #else - - vec2 vertex_base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0)); - vec2 vertex_base = vertex_base_arr[gl_VertexID]; + draw_data_instance = gl_VertexID / 6; + vec2 vertex_base_arr[6] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 1.0)); + vec2 vertex_base = vertex_base_arr[gl_VertexID % 6]; vec2 uv = draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw) * ((draw_data[draw_data_instance].flags & FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy); vec4 color = draw_data[draw_data_instance].modulation; @@ -127,6 +126,8 @@ void main() { } #endif + vec2 color_texture_pixel_size = draw_data[draw_data_instance].color_texture_pixel_size.xy; + #ifdef USE_POINT_SIZE float point_size = 1.0; #endif @@ -210,7 +211,9 @@ void main() { #include "canvas_uniforms_inc.glsl" #include "stdlib_inc.glsl" -//uniform sampler2D atlas_texture; //texunit:-2 +#ifndef DISABLE_LIGHTING +uniform sampler2D atlas_texture; //texunit:-2 +#endif // DISABLE_LIGHTING //uniform sampler2D shadow_atlas_texture; //texunit:-3 uniform sampler2D screen_texture; //texunit:-4 uniform sampler2D sdf_texture; //texunit:-5 @@ -242,6 +245,77 @@ layout(std140) uniform MaterialUniforms{ #endif #GLOBALS +#ifndef DISABLE_LIGHTING +#ifdef LIGHT_CODE_USED + +vec4 light_compute( + vec3 light_vertex, + vec3 light_position, + vec3 normal, + vec4 light_color, + float light_energy, + vec4 specular_shininess, + inout vec4 shadow_modulate, + vec2 screen_uv, + vec2 uv, + vec4 color, bool is_directional) { + vec4 light = vec4(0.0); + vec3 light_direction = vec3(0.0); + + if (is_directional) { + light_direction = normalize(mix(vec3(light_position.xy, 0.0), vec3(0, 0, 1), light_position.z)); + light_position = vec3(0.0); + } else { + light_direction = normalize(light_position - light_vertex); + } + +#CODE : LIGHT + + return light; +} + +#endif + +vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 light_color, vec4 specular_shininess, bool specular_shininess_used) { + float cNdotL = max(0.0, dot(normal, light_vec)); + + if (specular_shininess_used) { + //blinn + vec3 view = vec3(0.0, 0.0, 1.0); // not great but good enough + vec3 half_vec = normalize(view + light_vec); + + float cNdotV = max(dot(normal, view), 0.0); + float cNdotH = max(dot(normal, half_vec), 0.0); + float cVdotH = max(dot(view, half_vec), 0.0); + float cLdotH = max(dot(light_vec, half_vec), 0.0); + float shininess = exp2(15.0 * specular_shininess.a + 1.0) * 0.25; + float blinn = pow(cNdotH, shininess); + blinn *= (shininess + 8.0) * (1.0 / (8.0 * M_PI)); + float s = (blinn) / max(4.0 * cNdotV * cNdotL, 0.75); + + return specular_shininess.rgb * light_color * s + light_color * base_color * cNdotL; + } else { + return light_color * base_color * cNdotL; + } +} + +void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) { + uint blend_mode = light_array[light_base].flags & LIGHT_FLAGS_BLEND_MASK; + + switch (blend_mode) { + case LIGHT_FLAGS_BLEND_MODE_ADD: { + color.rgb += light_color.rgb * light_color.a; + } break; + case LIGHT_FLAGS_BLEND_MODE_SUB: { + color.rgb -= light_color.rgb * light_color.a; + } break; + case LIGHT_FLAGS_BLEND_MODE_MIX: { + color.rgb = mix(color.rgb, light_color.rgb, light_color.a); + } break; + } +} + +#endif #ifdef USE_NINEPATCH @@ -352,7 +426,8 @@ void main() { color *= texture(color_texture, uv); } - bool using_light = false; + uint light_count = (draw_data[draw_data_instance].flags >> uint(FLAGS_LIGHT_COUNT_SHIFT)) & uint(0xF); //max 16 lights + bool using_light = light_count > 0u || directional_light_count > 0u; vec3 normal; @@ -393,6 +468,8 @@ void main() { vec2 screen_uv = vec2(0.0); #endif + vec2 color_texture_pixel_size = draw_data[draw_data_instance].color_texture_pixel_size.xy; + vec3 light_vertex = vec3(vertex, 0.0); vec2 shadow_vertex = vertex; @@ -411,11 +488,105 @@ void main() { #endif } + if (normal_used) { + //convert by item transform + normal.xy = mat2(normalize(draw_data[draw_data_instance].world_x), normalize(draw_data[draw_data_instance].world_y)) * normal.xy; + //convert by canvas transform + normal = normalize((canvas_normal_transform * vec4(normal, 0.0)).xyz); + } + + vec4 base_color = color; + #ifdef MODE_LIGHT_ONLY color = vec4(0.0); #else color *= canvas_modulation; #endif +#if !defined(DISABLE_LIGHTING) && !defined(MODE_UNSHADED) + + // Directional Lights + + for (uint i = 0u; i < directional_light_count; i++) { + uint light_base = i; + + vec2 direction = light_array[light_base].position; + vec4 light_color = light_array[light_base].color; + +#ifdef LIGHT_CODE_USED + + vec4 shadow_modulate = vec4(1.0); + light_color = light_compute(light_vertex, vec3(direction, light_array[light_base].height), normal, light_color, light_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, true); +#else + + if (normal_used) { + vec3 light_vec = normalize(mix(vec3(direction, 0.0), vec3(0, 0, 1), light_array[light_base].height)); + light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used); + } else { + light_color.rgb *= base_color.rgb; + } +#endif + + light_blend_compute(light_base, light_color, color.rgb); + } + + // Positional Lights + + for (uint i = 0u; i < MAX_LIGHTS_PER_ITEM; i++) { + if (i >= light_count) { + break; + } + uint light_base; + if (i < 8u) { + if (i < 4u) { + light_base = draw_data[draw_data_instance].lights[0]; + } else { + light_base = draw_data[draw_data_instance].lights[1]; + } + } else { + if (i < 12u) { + light_base = draw_data[draw_data_instance].lights[2]; + } else { + light_base = draw_data[draw_data_instance].lights[3]; + } + } + light_base >>= (i & 3u) * 8u; + light_base &= uint(0xFF); + + vec2 tex_uv = (vec4(vertex, 0.0, 1.0) * mat4(light_array[light_base].texture_matrix[0], light_array[light_base].texture_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. + vec2 tex_uv_atlas = tex_uv * light_array[light_base].atlas_rect.zw + light_array[light_base].atlas_rect.xy; + vec4 light_color = textureLod(atlas_texture, tex_uv_atlas, 0.0); + vec4 light_base_color = light_array[light_base].color; + +#ifdef LIGHT_CODE_USED + + vec4 shadow_modulate = vec4(1.0); + vec3 light_position = vec3(light_array[light_base].position, light_array[light_base].height); + + light_color.rgb *= light_base_color.rgb; + light_color = light_compute(light_vertex, light_position, normal, light_color, light_base_color.a, specular_shininess, shadow_modulate, screen_uv, uv, base_color, false); +#else + + light_color.rgb *= light_base_color.rgb * light_base_color.a; + + if (normal_used) { + vec3 light_pos = vec3(light_array[light_base].position, light_array[light_base].height); + vec3 pos = light_vertex; + vec3 light_vec = normalize(light_pos - pos); + + light_color.rgb = light_normal_compute(light_vec, normal, base_color.rgb, light_color.rgb, specular_shininess, specular_shininess_used); + } else { + light_color.rgb *= base_color.rgb; + } +#endif + if (any(lessThan(tex_uv, vec2(0.0, 0.0))) || any(greaterThanEqual(tex_uv, vec2(1.0, 1.0)))) { + //if outside the light texture, light color is zero + light_color.a = 0.0; + } + + light_blend_compute(light_base, light_color, color.rgb); + } +#endif + frag_color = color; } diff --git a/drivers/gles3/shaders/canvas_uniforms_inc.glsl b/drivers/gles3/shaders/canvas_uniforms_inc.glsl index 6b65e09cbf..dd5ebecb1a 100644 --- a/drivers/gles3/shaders/canvas_uniforms_inc.glsl +++ b/drivers/gles3/shaders/canvas_uniforms_inc.glsl @@ -82,6 +82,7 @@ layout(std140) uniform CanvasData { //ubo:0 uint pad2; }; +#ifndef DISABLE_LIGHTING #define LIGHT_FLAGS_BLEND_MASK uint(3 << 16) #define LIGHT_FLAGS_BLEND_MODE_ADD uint(0 << 16) #define LIGHT_FLAGS_BLEND_MODE_SUB uint(1 << 16) @@ -94,6 +95,27 @@ layout(std140) uniform CanvasData { //ubo:0 #define LIGHT_FLAGS_SHADOW_PCF5 uint(1 << 22) #define LIGHT_FLAGS_SHADOW_PCF13 uint(2 << 22) +struct Light { + mat2x4 texture_matrix; //light to texture coordinate matrix (transposed) + mat2x4 shadow_matrix; //light to shadow coordinate matrix (transposed) + vec4 color; + + uint shadow_color; // packed + uint flags; //index to light texture + float shadow_pixel_size; + float height; + + vec2 position; + float shadow_zfar_inv; + float shadow_y_ofs; + + vec4 atlas_rect; +}; + +layout(std140) uniform LightData { //ubo:2 + Light light_array[MAX_LIGHTS]; +}; +#endif // DISABLE_LIGHTING layout(std140) uniform DrawDataInstances { //ubo:3 DrawData draw_data[MAX_DRAW_DATA_INSTANCES]; diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl index ca2fc7e36d..796ba79c2e 100644 --- a/drivers/gles3/shaders/copy.glsl +++ b/drivers/gles3/shaders/copy.glsl @@ -2,7 +2,7 @@ #[modes] mode_default = #define MODE_SIMPLE_COPY -mode_copy_section = #define USE_COPY_SECTION +mode_copy_section = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY mode_gaussian_blur = #define MODE_GAUSSIAN_BLUR mode_mipmap = #define MODE_MIPMAP mode_simple_color = #define MODE_SIMPLE_COLOR \n#define USE_COPY_SECTION @@ -25,8 +25,7 @@ void main() { gl_Position = vec4(vertex_attrib, 1.0, 1.0); #ifdef USE_COPY_SECTION - gl_Position.xy = (copy_section.xy + (uv_interp.xy * 0.5 + 0.5) * copy_section.zw) * 2.0 - 1.0; - uv_interp = copy_section.xy + uv_interp * copy_section.zw; + gl_Position.xy = (copy_section.xy + uv_interp.xy * copy_section.zw) * 2.0 - 1.0; #endif } diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index a23828bb8a..ed176c7829 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -16,6 +16,7 @@ DISABLE_LIGHT_OMNI = false DISABLE_LIGHT_SPOT = false DISABLE_FOG = false USE_RADIANCE_MAP = true +USE_MULTIVIEW = false #[vertex] @@ -153,6 +154,15 @@ layout(std140) uniform SceneData { // ubo:2 } scene_data; +#ifdef USE_MULTIVIEW +layout(std140) uniform MultiviewData { // ubo:8 + highp mat4 projection_matrix_view[MAX_VIEWS]; + highp mat4 inv_projection_matrix_view[MAX_VIEWS]; + highp vec4 eye_offset[MAX_VIEWS]; +} +multiview_data; +#endif + uniform highp mat4 world_transform; #ifdef USE_LIGHTMAP @@ -250,8 +260,14 @@ void main() { #if defined(OVERRIDE_POSITION) highp vec4 position; #endif - highp mat4 projection_matrix = scene_data.projection_matrix; - highp mat4 inv_projection_matrix = scene_data.inv_projection_matrix; + +#ifdef USE_MULTIVIEW + mat4 projection_matrix = multiview_data.projection_matrix_view[ViewIndex]; + mat4 inv_projection_matrix = multiview_data.inv_projection_matrix_view[ViewIndex]; +#else + mat4 projection_matrix = scene_data.projection_matrix; + mat4 inv_projection_matrix = scene_data.inv_projection_matrix; +#endif //USE_MULTIVIEW #ifdef USE_INSTANCING vec4 instance_custom = vec4(unpackHalf2x16(instance_color_custom_data.z), unpackHalf2x16(instance_color_custom_data.w)); @@ -339,7 +355,6 @@ void main() { /* clang-format off */ #[fragment] - // Default to SPECULAR_SCHLICK_GGX. #if !defined(SPECULAR_DISABLED) && !defined(SPECULAR_SCHLICK_GGX) && !defined(SPECULAR_TOON) #define SPECULAR_SCHLICK_GGX @@ -463,6 +478,15 @@ layout(std140) uniform SceneData { // ubo:2 } scene_data; +#ifdef USE_MULTIVIEW +layout(std140) uniform MultiviewData { // ubo:8 + highp mat4 projection_matrix_view[MAX_VIEWS]; + highp mat4 inv_projection_matrix_view[MAX_VIEWS]; + highp vec4 eye_offset[MAX_VIEWS]; +} +multiview_data; +#endif + /* clang-format off */ #GLOBALS @@ -530,8 +554,13 @@ uniform highp samplerCubeShadow positional_shadow; // texunit:-4 #endif // !defined(DISABLE_LIGHT_OMNI) && !defined(DISABLE_LIGHT_SPOT) -uniform highp sampler2D screen_texture; // texunit:-5 +#ifdef USE_MULTIVIEW +uniform highp sampler2DArray depth_buffer; // texunit:-6 +uniform highp sampler2DArray screen_texture; // texunit:-5 +#else uniform highp sampler2D depth_buffer; // texunit:-6 +uniform highp sampler2D screen_texture; // texunit:-5 +#endif uniform highp mat4 world_transform; uniform mediump float opaque_prepass_threshold; @@ -884,7 +913,11 @@ vec4 fog_process(vec3 vertex) { void main() { //lay out everything, whatever is unused is optimized away anyway vec3 vertex = vertex_interp; +#ifdef USE_MULTIVIEW + vec3 view = -normalize(vertex_interp - multiview_data.eye_offset[ViewIndex].xyz); +#else vec3 view = -normalize(vertex_interp); +#endif vec3 albedo = vec3(1.0); vec3 backlight = vec3(0.0); vec4 transmittance_color = vec4(0.0, 0.0, 0.0, 1.0); diff --git a/drivers/gles3/shaders/tonemap.glsl b/drivers/gles3/shaders/tonemap.glsl index a478cf9170..0b769e77f2 100644 --- a/drivers/gles3/shaders/tonemap.glsl +++ b/drivers/gles3/shaders/tonemap.glsl @@ -44,7 +44,11 @@ in vec2 uv_interp; layout(location = 0) out vec4 frag_color; +#ifdef USE_MULTIVIEW +uniform highp sampler2DArray source; //texunit:0 +#else uniform highp sampler2D source; //texunit:0 +#endif #if defined(USE_GLOW_LEVEL1) || defined(USE_GLOW_LEVEL2) || defined(USE_GLOW_LEVEL3) || defined(USE_GLOW_LEVEL4) || defined(USE_GLOW_LEVEL5) || defined(USE_GLOW_LEVEL6) || defined(USE_GLOW_LEVEL7) #define USING_GLOW // only use glow when at least one glow level is selected @@ -191,10 +195,17 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) { const float FXAA_REDUCE_MUL = (1.0 / 8.0); const float FXAA_SPAN_MAX = 8.0; +#ifdef USE_MULTIVIEW + vec3 rgbNW = textureLod(source, vec3(uv_interp + vec2(-1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz; + vec3 rgbNE = textureLod(source, vec3(uv_interp + vec2(1.0, -1.0) * pixel_size, ViewIndex), 0.0).xyz; + vec3 rgbSW = textureLod(source, vec3(uv_interp + vec2(-1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz; + vec3 rgbSE = textureLod(source, vec3(uv_interp + vec2(1.0, 1.0) * pixel_size, ViewIndex), 0.0).xyz; +#else vec3 rgbNW = textureLod(source, uv_interp + vec2(-1.0, -1.0) * pixel_size, 0.0).xyz; vec3 rgbNE = textureLod(source, uv_interp + vec2(1.0, -1.0) * pixel_size, 0.0).xyz; vec3 rgbSW = textureLod(source, uv_interp + vec2(-1.0, 1.0) * pixel_size, 0.0).xyz; vec3 rgbSE = textureLod(source, uv_interp + vec2(1.0, 1.0) * pixel_size, 0.0).xyz; +#endif vec3 rgbM = color; vec3 luma = vec3(0.299, 0.587, 0.114); float lumaNW = dot(rgbNW, luma); @@ -219,8 +230,13 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) { dir * rcpDirMin)) * pixel_size; +#ifdef USE_MULTIVIEW + vec3 rgbA = 0.5 * (textureLod(source, vec3(uv_interp + dir * (1.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * (2.0 / 3.0 - 0.5), ViewIndex), 0.0).xyz); + vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, vec3(uv_interp + dir * -0.5, ViewIndex), 0.0).xyz + textureLod(source, vec3(uv_interp + dir * 0.5, ViewIndex), 0.0).xyz); +#else vec3 rgbA = 0.5 * (textureLod(source, uv_interp + dir * (1.0 / 3.0 - 0.5), 0.0).xyz + textureLod(source, uv_interp + dir * (2.0 / 3.0 - 0.5), 0.0).xyz); vec3 rgbB = rgbA * 0.5 + 0.25 * (textureLod(source, uv_interp + dir * -0.5, 0.0).xyz + textureLod(source, uv_interp + dir * 0.5, 0.0).xyz); +#endif float lumaB = dot(rgbB, luma); if ((lumaB < lumaMin) || (lumaB > lumaMax)) { @@ -231,7 +247,11 @@ vec3 apply_fxaa(vec3 color, vec2 uv_interp, vec2 pixel_size) { } void main() { +#ifdef USE_MULTIVIEW + vec4 color = textureLod(source, vec3(uv_interp, ViewIndex), 0.0); +#else vec4 color = textureLod(source, uv_interp, 0.0); +#endif #ifdef USE_FXAA color.rgb = apply_fxaa(color.rgb, uv_interp, pixel_size); diff --git a/drivers/gles3/storage/config.cpp b/drivers/gles3/storage/config.cpp index 6cc65e7bb2..943df3b156 100644 --- a/drivers/gles3/storage/config.cpp +++ b/drivers/gles3/storage/config.cpp @@ -34,6 +34,15 @@ #include "core/config/project_settings.h" #include "core/templates/vector.h" +#ifdef ANDROID_ENABLED +#include <GLES3/gl3.h> +#include <GLES3/gl3ext.h> +#include <GLES3/gl3platform.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#endif + using namespace GLES3; #define _GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF @@ -85,6 +94,9 @@ Config::Config() { glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &max_texture_image_units); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &max_uniform_buffer_size); + glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &max_viewport_size); + + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_offset_alignment); // the use skeleton software path should be used if either float texture is not supported, // OR max_vertex_texture_image_units is zero @@ -96,6 +108,16 @@ Config::Config() { anisotropic_level = MIN(float(1 << int(ProjectSettings::get_singleton()->get("rendering/textures/default_filters/anisotropic_filtering_level"))), anisotropic_level); } + multiview_supported = extensions.has("GL_OVR_multiview2") || extensions.has("GL_OVR_multiview"); +#ifdef ANDROID_ENABLED + if (multiview_supported) { + eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR"); + if (eglFramebufferTextureMultiviewOVR == nullptr) { + multiview_supported = false; + } + } +#endif + force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading"); use_nearest_mip_filter = GLOBAL_GET("rendering/textures/default_filters/use_nearest_mipmap_filter"); diff --git a/drivers/gles3/storage/config.h b/drivers/gles3/storage/config.h index b83c83f425..d4b38acd18 100644 --- a/drivers/gles3/storage/config.h +++ b/drivers/gles3/storage/config.h @@ -44,6 +44,10 @@ #include OPENGL_INCLUDE_H #endif +#ifdef ANDROID_ENABLED +typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLint, GLsizei); +#endif + namespace GLES3 { class Config { @@ -60,10 +64,13 @@ public: int max_texture_image_units = 0; int max_texture_size = 0; int max_uniform_buffer_size = 0; + int max_viewport_size = 0; int max_renderable_elements = 0; int max_renderable_lights = 0; int max_lights_per_object = 0; + int uniform_buffer_offset_alignment = 0; + // TODO implement wireframe in OpenGL // bool generate_wireframes; @@ -80,6 +87,11 @@ public: bool support_anisotropic_filter = false; float anisotropic_level = 0.0f; + bool multiview_supported = false; +#ifdef ANDROID_ENABLED + PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC eglFramebufferTextureMultiviewOVR = nullptr; +#endif + static Config *get_singleton() { return singleton; }; Config(); diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index 6411590aee..b6bd4a2760 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -333,6 +333,46 @@ AABB LightStorage::light_get_aabb(RID p_light) const { ERR_FAIL_V(AABB()); } +/* LIGHT INSTANCE API */ + +RID LightStorage::light_instance_create(RID p_light) { + RID li = light_instance_owner.make_rid(LightInstance()); + + LightInstance *light_instance = light_instance_owner.get_or_null(li); + + light_instance->self = li; + light_instance->light = p_light; + light_instance->light_type = light_get_type(p_light); + + return li; +} + +void LightStorage::light_instance_free(RID p_light_instance) { + LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); + ERR_FAIL_COND(!light_instance); + light_instance_owner.free(p_light_instance); +} + +void LightStorage::light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) { + LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); + ERR_FAIL_COND(!light_instance); + + light_instance->transform = p_transform; +} + +void LightStorage::light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) { + LightInstance *light_instance = light_instance_owner.get_or_null(p_light_instance); + ERR_FAIL_COND(!light_instance); + + light_instance->aabb = p_aabb; +} + +void LightStorage::light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale, float p_range_begin, const Vector2 &p_uv_scale) { +} + +void LightStorage::light_instance_mark_visible(RID p_light_instance) { +} + /* PROBE API */ RID LightStorage::reflection_probe_allocate() { @@ -419,6 +459,53 @@ float LightStorage::reflection_probe_get_mesh_lod_threshold(RID p_probe) const { return 0.0; } +/* REFLECTION ATLAS */ + +RID LightStorage::reflection_atlas_create() { + return RID(); +} + +void LightStorage::reflection_atlas_free(RID p_ref_atlas) { +} + +int LightStorage::reflection_atlas_get_size(RID p_ref_atlas) const { + return 0; +} + +void LightStorage::reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) { +} + +/* REFLECTION PROBE INSTANCE */ + +RID LightStorage::reflection_probe_instance_create(RID p_probe) { + return RID(); +} + +void LightStorage::reflection_probe_instance_free(RID p_instance) { +} + +void LightStorage::reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) { +} + +void LightStorage::reflection_probe_release_atlas_index(RID p_instance) { +} + +bool LightStorage::reflection_probe_instance_needs_redraw(RID p_instance) { + return false; +} + +bool LightStorage::reflection_probe_instance_has_reflection(RID p_instance) { + return false; +} + +bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) { + return false; +} + +bool LightStorage::reflection_probe_instance_postprocess_step(RID p_instance) { + return true; +} + /* LIGHTMAP CAPTURE */ RID LightStorage::lightmap_allocate() { @@ -484,6 +571,18 @@ float LightStorage::lightmap_get_probe_capture_update_speed() const { return 0; } +/* LIGHTMAP INSTANCE */ + +RID LightStorage::lightmap_instance_create(RID p_lightmap) { + return RID(); +} + +void LightStorage::lightmap_instance_free(RID p_lightmap) { +} + +void LightStorage::lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) { +} + /* LIGHT SHADOW MAPPING */ /* @@ -584,4 +683,36 @@ void LightStorage::canvas_light_occluder_set_polylines(RID p_occluder, const Poo } */ +/* SHADOW ATLAS API */ + +RID LightStorage::shadow_atlas_create() { + return RID(); +} + +void LightStorage::shadow_atlas_free(RID p_atlas) { +} + +void LightStorage::shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits) { +} + +void LightStorage::shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) { +} + +bool LightStorage::shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) { + return false; +} + +void LightStorage::shadow_atlas_update(RID p_atlas) { +} + +void LightStorage::directional_shadow_atlas_set_size(int p_size, bool p_16_bits) { +} + +int LightStorage::get_directional_light_shadow_size(RID p_light_intance) { + return 0; +} + +void LightStorage::set_directional_shadow_count(int p_count) { +} + #endif // !GLES3_ENABLED diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index f054f0fdc6..12cb2c4393 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -36,6 +36,7 @@ #include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" #include "core/templates/self_list.h" +#include "drivers/gles3/storage/texture_storage.h" #include "servers/rendering/renderer_compositor.h" #include "servers/rendering/storage/light_storage.h" #include "servers/rendering/storage/utilities.h" @@ -75,6 +76,33 @@ struct Light { Dependency dependency; }; +/* Light instance */ +struct LightInstance { + RS::LightType light_type = RS::LIGHT_DIRECTIONAL; + + AABB aabb; + RID self; + RID light; + Transform3D transform; + + Vector3 light_vector; + Vector3 spot_vector; + float linear_att = 0.0; + + uint64_t shadow_pass = 0; + uint64_t last_scene_pass = 0; + uint64_t last_scene_shadow_pass = 0; + uint64_t last_pass = 0; + uint32_t cull_mask = 0; + uint32_t light_directional_index = 0; + + Rect2 directional_rect; + + uint32_t gl_id = -1; + + LightInstance() {} +}; + /* REFLECTION PROBE */ struct ReflectionProbe { @@ -127,6 +155,9 @@ private: /* LIGHT */ mutable RID_Owner<Light, true> light_owner; + /* Light instance */ + mutable RID_Owner<LightInstance> light_instance_owner; + /* REFLECTION PROBE */ mutable RID_Owner<ReflectionProbe, true> reflection_probe_owner; @@ -246,7 +277,7 @@ public: const Light *light = light_owner.get_or_null(p_light); ERR_FAIL_COND_V(!light, RS::LIGHT_DIRECTIONAL); - return light_owner.owns(light->projector); + return TextureStorage::get_singleton()->owns_texture(light->projector); } _FORCE_INLINE_ bool light_is_negative(RID p_light) const { @@ -267,6 +298,28 @@ public: virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) override { return 0; } virtual uint64_t light_get_version(RID p_light) const override; + /* LIGHT INSTANCE API */ + + LightInstance *get_light_instance(RID p_rid) { return light_instance_owner.get_or_null(p_rid); }; + bool owns_light_instance(RID p_rid) { return light_instance_owner.owns(p_rid); }; + + virtual RID light_instance_create(RID p_light) override; + virtual void light_instance_free(RID p_light_instance) override; + + virtual void light_instance_set_transform(RID p_light_instance, const Transform3D &p_transform) override; + virtual void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) override; + virtual void light_instance_set_shadow_transform(RID p_light_instance, const Projection &p_projection, const Transform3D &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) override; + virtual void light_instance_mark_visible(RID p_light_instance) override; + + _FORCE_INLINE_ RS::LightType light_instance_get_type(RID p_light_instance) { + LightInstance *li = light_instance_owner.get_or_null(p_light_instance); + return li->light_type; + } + _FORCE_INLINE_ uint32_t light_instance_get_gl_id(RID p_light_instance) { + LightInstance *li = light_instance_owner.get_or_null(p_light_instance); + return li->gl_id; + } + /* PROBE API */ virtual RID reflection_probe_allocate() override; @@ -297,6 +350,24 @@ public: virtual float reflection_probe_get_origin_max_distance(RID p_probe) const override; virtual bool reflection_probe_renders_shadows(RID p_probe) const override; + /* REFLECTION ATLAS */ + + virtual RID reflection_atlas_create() override; + virtual void reflection_atlas_free(RID p_ref_atlas) override; + virtual int reflection_atlas_get_size(RID p_ref_atlas) const override; + virtual void reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) override; + + /* REFLECTION PROBE INSTANCE */ + + virtual RID reflection_probe_instance_create(RID p_probe) override; + virtual void reflection_probe_instance_free(RID p_instance) override; + virtual void reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) override; + virtual void reflection_probe_release_atlas_index(RID p_instance) override; + virtual bool reflection_probe_instance_needs_redraw(RID p_instance) override; + virtual bool reflection_probe_instance_has_reflection(RID p_instance) override; + virtual bool reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) override; + virtual bool reflection_probe_instance_postprocess_step(RID p_instance) override; + /* LIGHTMAP CAPTURE */ Lightmap *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); }; @@ -337,6 +408,26 @@ public: RID canvas_light_occluder_create(); void canvas_light_occluder_set_polylines(RID p_occluder, const LocalVector<Vector2> &p_lines); */ + + /* LIGHTMAP INSTANCE */ + + virtual RID lightmap_instance_create(RID p_lightmap) override; + virtual void lightmap_instance_free(RID p_lightmap) override; + virtual void lightmap_instance_set_transform(RID p_lightmap, const Transform3D &p_transform) override; + + /* SHADOW ATLAS API */ + + virtual RID shadow_atlas_create() override; + virtual void shadow_atlas_free(RID p_atlas) override; + virtual void shadow_atlas_set_size(RID p_atlas, int p_size, bool p_16_bits = true) override; + virtual void shadow_atlas_set_quadrant_subdivision(RID p_atlas, int p_quadrant, int p_subdivision) override; + virtual bool shadow_atlas_update_light(RID p_atlas, RID p_light_intance, float p_coverage, uint64_t p_light_version) override; + + virtual void shadow_atlas_update(RID p_atlas) override; + + virtual void directional_shadow_atlas_set_size(int p_size, bool p_16_bits = true) override; + virtual int get_directional_light_shadow_size(RID p_light_intance) override; + virtual void set_directional_shadow_count(int p_count) override; }; } // namespace GLES3 diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 687e98ba58..f5241e33ab 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -714,7 +714,7 @@ _FORCE_INLINE_ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataTy Projection v = value; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { - gui[i * 4 + j] = v.matrix[i][j]; + gui[i * 4 + j] = v.columns[i][j]; } } } @@ -897,7 +897,9 @@ _FORCE_INLINE_ static void _fill_std140_ubo_empty(ShaderLanguage::DataType type, case ShaderLanguage::TYPE_BVEC3: case ShaderLanguage::TYPE_IVEC3: case ShaderLanguage::TYPE_UVEC3: - case ShaderLanguage::TYPE_VEC3: + case ShaderLanguage::TYPE_VEC3: { + memset(data, 0, 12 * p_array_size); + } break; case ShaderLanguage::TYPE_BVEC4: case ShaderLanguage::TYPE_IVEC4: case ShaderLanguage::TYPE_UVEC4: @@ -1393,7 +1395,7 @@ MaterialStorage::MaterialStorage() { actions.renames["NORMAL_MAP"] = "normal_map"; actions.renames["NORMAL_MAP_DEPTH"] = "normal_map_depth"; actions.renames["TEXTURE"] = "color_texture"; - actions.renames["TEXTURE_PIXEL_SIZE"] = "draw_data.color_texture_pixel_size"; + actions.renames["TEXTURE_PIXEL_SIZE"] = "color_texture_pixel_size"; actions.renames["NORMAL_TEXTURE"] = "normal_texture"; actions.renames["SPECULAR_SHININESS_TEXTURE"] = "specular_texture"; actions.renames["SPECULAR_SHININESS"] = "specular_shininess"; @@ -1406,6 +1408,8 @@ MaterialStorage::MaterialStorage() { actions.renames["VERTEX_ID"] = "gl_VertexIndex"; actions.renames["LIGHT_POSITION"] = "light_position"; + actions.renames["LIGHT_DIRECTION"] = "light_direction"; + actions.renames["LIGHT_IS_DIRECTIONAL"] = "is_directional"; actions.renames["LIGHT_COLOR"] = "light_color"; actions.renames["LIGHT_ENERGY"] = "light_energy"; actions.renames["LIGHT"] = "light"; @@ -1654,7 +1658,7 @@ ShaderCompiler::DefaultIdentifierActions actions; actions.render_mode_defines["disable_force"] = "#define DISABLE_FORCE\n"; actions.render_mode_defines["disable_velocity"] = "#define DISABLE_VELOCITY\n"; actions.render_mode_defines["keep_data"] = "#define ENABLE_KEEP_DATA\n"; - actions.render_mode_defines["collision_use_scale"] = "#define USE_COLLISON_SCALE\n"; + actions.render_mode_defines["collision_use_scale"] = "#define USE_COLLISION_SCALE\n"; actions.sampler_array_name = "material_samplers"; actions.base_texture_binding_index = 1; diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index 65c46631ed..6504c7748c 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -494,7 +494,7 @@ public: static _FORCE_INLINE_ void store_camera(const Projection &p_mtx, float *p_array) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { - p_array[i * 4 + j] = p_mtx.matrix[i][j]; + p_array[i * 4 + j] = p_mtx.columns[i][j]; } } } diff --git a/drivers/gles3/storage/mesh_storage.cpp b/drivers/gles3/storage/mesh_storage.cpp index 22d84eba93..11ce31856d 100644 --- a/drivers/gles3/storage/mesh_storage.cpp +++ b/drivers/gles3/storage/mesh_storage.cpp @@ -309,12 +309,48 @@ RS::BlendShapeMode MeshStorage::mesh_get_blend_shape_mode(RID p_mesh) const { } void MeshStorage::mesh_surface_update_vertex_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + ERR_FAIL_COND(p_data.size() == 0); + + uint64_t data_size = p_data.size(); + ERR_FAIL_COND(p_offset + data_size > mesh->surfaces[p_surface]->vertex_buffer_size); + const uint8_t *r = p_data.ptr(); + + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->vertex_buffer); + glBufferSubData(GL_ARRAY_BUFFER, p_offset, data_size, r); + glBindBuffer(GL_ARRAY_BUFFER, 0); } void MeshStorage::mesh_surface_update_attribute_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + ERR_FAIL_COND(p_data.size() == 0); + + uint64_t data_size = p_data.size(); + ERR_FAIL_COND(p_offset + data_size > mesh->surfaces[p_surface]->attribute_buffer_size); + const uint8_t *r = p_data.ptr(); + + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->attribute_buffer); + glBufferSubData(GL_ARRAY_BUFFER, p_offset, data_size, r); + glBindBuffer(GL_ARRAY_BUFFER, 0); } void MeshStorage::mesh_surface_update_skin_region(RID p_mesh, int p_surface, int p_offset, const Vector<uint8_t> &p_data) { + Mesh *mesh = mesh_owner.get_or_null(p_mesh); + ERR_FAIL_COND(!mesh); + ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_surface, mesh->surface_count); + ERR_FAIL_COND(p_data.size() == 0); + + uint64_t data_size = p_data.size(); + ERR_FAIL_COND(p_offset + data_size > mesh->surfaces[p_surface]->skin_buffer_size); + const uint8_t *r = p_data.ptr(); + + glBindBuffer(GL_ARRAY_BUFFER, mesh->surfaces[p_surface]->skin_buffer); + glBufferSubData(GL_ARRAY_BUFFER, p_offset, data_size, r); + glBindBuffer(GL_ARRAY_BUFFER, 0); } void MeshStorage::mesh_surface_set_material(RID p_mesh, int p_surface, RID p_material) { diff --git a/drivers/gles3/storage/mesh_storage.h b/drivers/gles3/storage/mesh_storage.h index 74f5800795..a31db24f2d 100644 --- a/drivers/gles3/storage/mesh_storage.h +++ b/drivers/gles3/storage/mesh_storage.h @@ -325,13 +325,12 @@ public: return s->index_count ? s->index_count : s->vertex_count; } - _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t *r_index_count = nullptr) const { + _FORCE_INLINE_ uint32_t mesh_surface_get_lod(void *p_surface, float p_model_scale, float p_distance_threshold, float p_mesh_lod_threshold, uint32_t &r_index_count) const { Mesh::Surface *s = reinterpret_cast<Mesh::Surface *>(p_surface); int32_t current_lod = -1; - if (r_index_count) { - *r_index_count = s->index_count; - } + r_index_count = s->index_count; + for (uint32_t i = 0; i < s->lod_count; i++) { float screen_size = s->lods[i].edge_length * p_model_scale / p_distance_threshold; if (screen_size > p_mesh_lod_threshold) { @@ -342,9 +341,7 @@ public: if (current_lod == -1) { return 0; } else { - if (r_index_count) { - *r_index_count = s->lods[current_lod].index_count; - } + r_index_count = s->lods[current_lod].index_count; return current_lod + 1; } } diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp index 9123984dc7..b8e4530f56 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -33,12 +33,17 @@ #include "render_scene_buffers_gles3.h" #include "texture_storage.h" +#ifdef ANDROID_ENABLED +#define glFramebufferTextureMultiviewOVR GLES3::Config::get_singleton()->eglFramebufferTextureMultiviewOVR +#endif + RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() { free_render_buffer_data(); } void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_internal_size, const Size2i p_target_size, float p_fsr_sharpness, float p_texture_mipmap_bias, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_taa, bool p_use_debanding, uint32_t p_view_count) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); //internal_size.x = p_internal_size.x; // ignore for now //internal_size.y = p_internal_size.y; @@ -50,7 +55,7 @@ void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_inte //msaa = p_msaa; //screen_space_aa = p_screen_space_aa; //use_debanding = p_use_debanding; - //view_count = p_view_count; + view_count = p_view_count; free_render_buffer_data(); @@ -62,24 +67,43 @@ void RenderSceneBuffersGLES3::configure(RID p_render_target, const Size2i p_inte glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); - glBindTexture(GL_TEXTURE_2D, rt->color); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + if (view_count > 1 && config->multiview_supported) { + glBindTexture(GL_TEXTURE_2D_ARRAY, rt->color); + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rt->color, 0, 0, view_count); + } else { + glBindTexture(GL_TEXTURE_2D, rt->color); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, rt->color, 0); + } glGenTextures(1, &depth_texture); - glBindTexture(GL_TEXTURE_2D, depth_texture); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 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_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (view_count > 1 && config->multiview_supported) { + glBindTexture(GL_TEXTURE_2D_ARRAY, depth_texture); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, view_count, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr); + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } else { + glBindTexture(GL_TEXTURE_2D, depth_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, rt->size.x, rt->size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 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_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0); + if (view_count > 1 && config->multiview_supported) { + glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_texture, 0, 0, view_count); + } else { + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_texture, 0); + } GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); glBindFramebuffer(GL_FRAMEBUFFER, texture_storage->system_fbo); if (status != GL_FRAMEBUFFER_COMPLETE) { diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h index ad0d2032b0..dbedbd22c3 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.h +++ b/drivers/gles3/storage/render_scene_buffers_gles3.h @@ -56,7 +56,7 @@ public: RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; //RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; //bool use_debanding = false; - //uint32_t view_count = 1; + uint32_t view_count = 1; bool is_transparent = false; diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index a801b3285a..524ff14cc9 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,22 @@ 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); + #ifdef GLES_OVER_GL glEnable(GL_PROGRAM_POINT_SIZE); #endif @@ -207,6 +217,11 @@ 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; } //TODO, move back to storage @@ -653,7 +668,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,6 +681,8 @@ 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(); @@ -707,8 +724,7 @@ void TextureStorage::texture_proxy_initialize(RID p_texture, RID p_base) { } 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); @@ -722,9 +738,7 @@ void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) { 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 +747,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 +766,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; @@ -810,9 +820,7 @@ 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); @@ -873,7 +881,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) { @@ -1007,7 +1015,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 +1072,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 +1153,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 +1404,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,6 +1432,8 @@ 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_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; @@ -1218,17 +1455,29 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { // color glGenTextures(1, &rt->color); - glBindTexture(GL_TEXTURE_2D, rt->color); - - glTexImage2D(GL_TEXTURE_2D, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + if (rt->view_count > 1 && config->multiview_supported) { + glBindTexture(GL_TEXTURE_2D_ARRAY, rt->color); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, rt->color_internal_format, rt->size.x, rt->size.y, rt->view_count, 0, rt->color_format, rt->color_type, nullptr); + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } else { + glBindTexture(GL_TEXTURE_2D, rt->color); + glTexImage2D(GL_TEXTURE_2D, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr); - 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_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, rt->color, 0); + if (rt->view_count > 1 && config->multiview_supported) { + 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); @@ -1247,8 +1496,15 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { texture->format = rt->image_format; texture->real_format = rt->image_format; - texture->type = Texture::TYPE_2D; - texture->target = GL_TEXTURE_2D; + if (rt->view_count > 1 && config->multiview_supported) { + texture->type = Texture::TYPE_LAYERED; + texture->target = GL_TEXTURE_2D_ARRAY; + texture->layers = rt->view_count; + } else { + texture->type = Texture::TYPE_2D; + texture->target = GL_TEXTURE_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; @@ -1329,24 +1585,6 @@ void TextureStorage::_clear_render_target(RenderTarget *rt) { rt->fbo = 0; 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); - - rt->external.fbo = 0; - } - */ Texture *tex = get_texture(rt->texture); tex->alloc_height = 0; @@ -1398,25 +1636,33 @@ 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; } _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; } @@ -1425,105 +1671,7 @@ 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->external.fbo == 0) { - return rt->texture; - } else { - return rt->external.texture; - } -} - -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_texture_id == 0) { - if (rt->external.fbo != 0) { - // free this - glDeleteFramebuffers(1, &rt->external.fbo); - - // and this - if (rt->external.depth != 0) { - glDeleteRenderbuffers(1, &rt->external.depth); - } - - // 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); - - } else { - // bind our frame buffer - glBindFramebuffer(GL_FRAMEBUFFER, rt->external.fbo); - - // find our texture - t = get_texture(rt->external.texture); - } - - // set our texture - t->tex_id = p_texture_id; - rt->external.color = p_texture_id; - - // 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; - - // 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); - } - - // check status and unbind - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo); - - if (status != GL_FRAMEBUFFER_COMPLETE) { - WARN_PRINT("framebuffer fail, status: " + get_framebuffer_error(status)); - } - - ERR_FAIL_COND(status != GL_FRAMEBUFFER_COMPLETE); - } + return rt->texture; } void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_transparent) { @@ -1536,6 +1684,13 @@ void TextureStorage::render_target_set_transparent(RID p_render_target, bool p_t _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) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_COND(!rt); @@ -1550,7 +1705,14 @@ void TextureStorage::render_target_set_direct_to_screen(RID p_render_target, boo _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); @@ -1577,6 +1739,13 @@ void TextureStorage::render_target_set_msaa(RID p_render_target, RS::ViewportMSA _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); @@ -1607,9 +1776,11 @@ 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) { diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 4f4032723b..dbe39428ac 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -126,11 +126,6 @@ struct CanvasTexture { RS::CanvasItemTextureFilter texture_filter = RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT; RS::CanvasItemTextureRepeat texture_repeat = RS::CANVAS_ITEM_TEXTURE_REPEAT_DEFAULT; - - Size2i size_cache = Size2i(1, 1); - bool use_normal_cache = false; - bool use_specular_cache = false; - bool cleared_cache = true; }; /* CANVAS SHADOW */ @@ -327,18 +322,9 @@ private: }; struct RenderTarget { - struct External { - GLuint fbo = 0; - GLuint color = 0; - GLuint depth = 0; - RID texture; - - External() { - } - } external; - Point2i position = Point2i(0, 0); Size2i size = Size2i(0, 0); + uint32_t view_count = 1; int mipmap_count = 1; RID self; GLuint fbo = 0; @@ -386,6 +372,38 @@ private: Ref<Image> _get_gl_image_and_format(const Ref<Image> &p_image, Image::Format p_format, Image::Format &r_real_format, GLenum &r_gl_format, GLenum &r_gl_internal_format, GLenum &r_gl_type, bool &r_compressed, bool p_force_decompress) const; + /* TEXTURE ATLAS API */ + + struct TextureAtlas { + struct Texture { + int users; + Rect2 uv_rect; + }; + + struct SortItem { + RID texture; + Size2i pixel_size; + Size2i size; + Point2i pos; + + bool operator<(const SortItem &p_item) const { + //sort larger to smaller + if (size.height == p_item.size.height) { + return size.width > p_item.size.width; + } else { + return size.height > p_item.size.height; + } + } + }; + + HashMap<RID, Texture> textures; + bool dirty = true; + + GLuint texture = 0; + GLuint framebuffer = 0; + Size2i size; + } texture_atlas; + /* Render Target API */ mutable RID_Owner<RenderTarget> render_target_owner; @@ -488,6 +506,25 @@ public: void texture_bind(RID p_texture, uint32_t p_texture_no); RID texture_create_radiance_cubemap(RID p_source, int p_resolution = -1) const; + /* TEXTURE ATLAS API */ + + void update_texture_atlas(); + + GLuint texture_atlas_get_texture() const; + _FORCE_INLINE_ Rect2 texture_atlas_get_texture_rect(RID p_texture) { + TextureAtlas::Texture *t = texture_atlas.textures.getptr(p_texture); + if (!t) { + return Rect2(); + } + + return t->uv_rect; + } + + void texture_add_to_texture_atlas(RID p_texture); + void texture_remove_from_texture_atlas(RID p_texture); + void texture_atlas_mark_dirty_on_texture(RID p_texture); + void texture_atlas_remove_texture(RID p_texture); + /* DECAL API */ virtual RID decal_allocate() override; @@ -509,6 +546,12 @@ public: virtual void texture_add_to_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {} virtual void texture_remove_from_decal_atlas(RID p_texture, bool p_panorama_to_dp = false) override {} + /* DECAL INSTANCE */ + + virtual RID decal_instance_create(RID p_decal) override; + virtual void decal_instance_free(RID p_decal_instance) override; + virtual void decal_instance_set_transform(RID p_decal, const Transform3D &p_transform) override; + /* RENDER TARGET API */ static GLuint system_fbo; @@ -518,17 +561,19 @@ public: virtual RID render_target_create() override; virtual void render_target_free(RID p_rid) override; + virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) override; + virtual Point2i render_target_get_position(RID p_render_target) const override; virtual void render_target_set_size(RID p_render_target, int p_width, int p_height, uint32_t p_view_count) override; - Size2i render_target_get_size(RID p_render_target); - virtual RID render_target_get_texture(RID p_render_target) override; - virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) override; - + virtual Size2i render_target_get_size(RID p_render_target) const override; virtual void render_target_set_transparent(RID p_render_target, bool p_is_transparent) override; + virtual bool render_target_get_transparent(RID p_render_target) const override; virtual void render_target_set_direct_to_screen(RID p_render_target, bool p_direct_to_screen) override; - virtual bool render_target_was_used(RID p_render_target) override; + virtual bool render_target_get_direct_to_screen(RID p_render_target) const override; + virtual bool render_target_was_used(RID p_render_target) const override; void render_target_clear_used(RID p_render_target); virtual void render_target_set_msaa(RID p_render_target, RS::ViewportMSAA p_msaa) override; + virtual RS::ViewportMSAA render_target_get_msaa(RID p_render_target) const override; // new void render_target_set_as_unused(RID p_render_target) override { @@ -548,8 +593,20 @@ public: void render_target_copy_to_back_buffer(RID p_render_target, const Rect2i &p_region, bool p_gen_mipmaps); void render_target_clear_back_buffer(RID p_render_target, const Rect2i &p_region, const Color &p_color); void render_target_gen_back_buffer_mipmaps(RID p_render_target, const Rect2i &p_region); - virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override{}; - virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override{}; + + virtual void render_target_set_vrs_mode(RID p_render_target, RS::ViewportVRSMode p_mode) override {} + virtual RS::ViewportVRSMode render_target_get_vrs_mode(RID p_render_target) const override { return RS::VIEWPORT_VRS_DISABLED; } + virtual void render_target_set_vrs_texture(RID p_render_target, RID p_texture) override {} + virtual RID render_target_get_vrs_texture(RID p_render_target) const override { return RID(); } + + virtual void render_target_set_override_color(RID p_render_target, RID p_texture) override {} + virtual RID render_target_get_override_color(RID p_render_target) const override { return RID(); } + virtual void render_target_set_override_depth(RID p_render_target, RID p_texture) override {} + virtual RID render_target_get_override_depth(RID p_render_target) const override { return RID(); } + virtual void render_target_set_override_velocity(RID p_render_target, RID p_texture) override {} + virtual RID render_target_get_override_velocity(RID p_render_target) const override { return RID(); } + + virtual RID render_target_get_texture(RID p_render_target) override; void bind_framebuffer(GLuint framebuffer) { glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp index 16bacf1829..0a04f0d4d0 100644 --- a/drivers/gles3/storage/utilities.cpp +++ b/drivers/gles3/storage/utilities.cpp @@ -302,6 +302,7 @@ void Utilities::update_dirty_resources() { MaterialStorage::get_singleton()->_update_queued_materials(); //MeshStorage::get_singleton()->_update_dirty_skeletons(); MeshStorage::get_singleton()->_update_dirty_multimeshes(); + TextureStorage::get_singleton()->update_texture_atlas(); } void Utilities::set_debug_generate_wireframes(bool p_generate) { @@ -355,4 +356,13 @@ String Utilities::get_video_adapter_api_version() const { return (const char *)glGetString(GL_VERSION); } +Size2i Utilities::get_maximum_viewport_size() const { + Config *config = Config::get_singleton(); + if (!config) { + return Size2i(); + } + + return Size2i(config->max_viewport_size, config->max_viewport_size); +} + #endif // GLES3_ENABLED diff --git a/drivers/gles3/storage/utilities.h b/drivers/gles3/storage/utilities.h index e054f2f816..936ac35cc9 100644 --- a/drivers/gles3/storage/utilities.h +++ b/drivers/gles3/storage/utilities.h @@ -150,6 +150,8 @@ public: virtual String get_video_adapter_vendor() const override; virtual RenderingDevice::DeviceType get_video_adapter_type() const override; virtual String get_video_adapter_api_version() const override; + + virtual Size2i get_maximum_viewport_size() const override; }; } // namespace GLES3 diff --git a/drivers/png/image_loader_png.cpp b/drivers/png/image_loader_png.cpp index 8d2f8a7ed6..165de34c71 100644 --- a/drivers/png/image_loader_png.cpp +++ b/drivers/png/image_loader_png.cpp @@ -36,7 +36,7 @@ #include <string.h> -Error ImageLoaderPNG::load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale) { +Error ImageLoaderPNG::load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) { const uint64_t buffer_size = f->get_length(); Vector<uint8_t> file_buffer; Error err = file_buffer.resize(buffer_size); diff --git a/drivers/png/image_loader_png.h b/drivers/png/image_loader_png.h index 91c3c8925f..a247d77310 100644 --- a/drivers/png/image_loader_png.h +++ b/drivers/png/image_loader_png.h @@ -40,7 +40,7 @@ private: static Ref<Image> load_mem_png(const uint8_t *p_png, int p_size); public: - virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, uint32_t p_flags, float p_scale); + virtual Error load_image(Ref<Image> p_image, Ref<FileAccess> f, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale); virtual void get_recognized_extensions(List<String> *p_extensions) const; ImageLoaderPNG(); }; diff --git a/drivers/png/png_driver_common.cpp b/drivers/png/png_driver_common.cpp index bc4bb3782b..79641464d8 100644 --- a/drivers/png/png_driver_common.cpp +++ b/drivers/png/png_driver_common.cpp @@ -119,7 +119,7 @@ Error png_to_image(const uint8_t *p_source, size_t p_size, bool p_force_linear, ERR_FAIL_COND_V(!success, ERR_FILE_CORRUPT); //print_line("png width: "+itos(png_img.width)+" height: "+itos(png_img.height)); - p_image->create(png_img.width, png_img.height, false, dest_format, buffer); + p_image->set_data(png_img.width, png_img.height, false, dest_format, buffer); return OK; } diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.h b/drivers/pulseaudio/audio_driver_pulseaudio.h index 85e328b49f..ae6e0acc97 100644 --- a/drivers/pulseaudio/audio_driver_pulseaudio.h +++ b/drivers/pulseaudio/audio_driver_pulseaudio.h @@ -48,8 +48,8 @@ class AudioDriverPulseAudio : public AudioDriver { pa_context *pa_ctx = nullptr; pa_stream *pa_str = nullptr; pa_stream *pa_rec_str = nullptr; - pa_channel_map pa_map; - pa_channel_map pa_rec_map; + pa_channel_map pa_map = {}; + pa_channel_map pa_rec_map = {}; String device_name = "Default"; String new_device = "Default"; diff --git a/drivers/register_driver_types.cpp b/drivers/register_driver_types.cpp index 504ef9843a..53a7f7aa4f 100644 --- a/drivers/register_driver_types.cpp +++ b/drivers/register_driver_types.cpp @@ -34,11 +34,11 @@ #include "drivers/png/image_loader_png.h" #include "drivers/png/resource_saver_png.h" -static ImageLoaderPNG *image_loader_png; +static Ref<ImageLoaderPNG> image_loader_png; static Ref<ResourceSaverPNG> resource_saver_png; void register_core_driver_types() { - image_loader_png = memnew(ImageLoaderPNG); + image_loader_png.instantiate(); ImageLoader::add_image_format_loader(image_loader_png); resource_saver_png.instantiate(); @@ -46,9 +46,8 @@ void register_core_driver_types() { } void unregister_core_driver_types() { - if (image_loader_png) { - memdelete(image_loader_png); - } + ImageLoader::remove_image_format_loader(image_loader_png); + image_loader_png.unref(); ResourceSaver::remove_resource_format_saver(resource_saver_png); resource_saver_png.unref(); diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp index 55ea952696..d894ceba59 100644 --- a/drivers/unix/dir_access_unix.cpp +++ b/drivers/unix/dir_access_unix.cpp @@ -30,7 +30,7 @@ #include "dir_access_unix.h" -#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) +#if defined(UNIX_ENABLED) #include "core/os/memory.h" #include "core/os/os.h" @@ -41,10 +41,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> - -#ifndef ANDROID_ENABLED #include <sys/statvfs.h> -#endif #ifdef HAVE_MNTENT #include <mntent.h> @@ -74,7 +71,7 @@ bool DirAccessUnix::file_exists(String p_file) { p_file = fix_path(p_file); - struct stat flags; + struct stat flags = {}; bool success = (stat(p_file.utf8().get_data(), &flags) == 0); if (success && S_ISDIR(flags.st_mode)) { @@ -93,7 +90,7 @@ bool DirAccessUnix::dir_exists(String p_dir) { p_dir = fix_path(p_dir); - struct stat flags; + struct stat flags = {}; bool success = (stat(p_dir.utf8().get_data(), &flags) == 0); return (success && S_ISDIR(flags.st_mode)); @@ -128,7 +125,7 @@ uint64_t DirAccessUnix::get_modified_time(String p_file) { p_file = fix_path(p_file); - struct stat flags; + struct stat flags = {}; bool success = (stat(p_file.utf8().get_data(), &flags) == 0); if (success) { @@ -161,7 +158,7 @@ String DirAccessUnix::get_next() { if (entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK) { String f = current_dir.path_join(fname); - struct stat flags; + struct stat flags = {}; if (stat(f.utf8().get_data(), &flags) == 0) { _cisdir = S_ISDIR(flags.st_mode); } else { @@ -415,7 +412,7 @@ Error DirAccessUnix::remove(String p_path) { p_path = fix_path(p_path); - struct stat flags; + struct stat flags = {}; if ((stat(p_path.utf8().get_data(), &flags) != 0)) { return FAILED; } @@ -434,7 +431,7 @@ bool DirAccessUnix::is_link(String p_file) { p_file = fix_path(p_file); - struct stat flags; + struct stat flags = {}; if ((lstat(p_file.utf8().get_data(), &flags) != 0)) { return FAILED; } @@ -475,17 +472,12 @@ Error DirAccessUnix::create_link(String p_source, String p_target) { } uint64_t DirAccessUnix::get_space_left() { -#ifndef NO_STATVFS struct statvfs vfs; if (statvfs(current_dir.utf8().get_data(), &vfs) != 0) { return 0; } return (uint64_t)vfs.f_bavail * (uint64_t)vfs.f_frsize; -#else - // FIXME: Implement this. - return 0; -#endif } String DirAccessUnix::get_filesystem_type() const { @@ -516,4 +508,4 @@ DirAccessUnix::~DirAccessUnix() { list_dir_end(); } -#endif // UNIX_ENABLED || LIBC_FILEIO_ENABLED +#endif // UNIX_ENABLED diff --git a/drivers/unix/dir_access_unix.h b/drivers/unix/dir_access_unix.h index f5dca7c282..4db24a27b9 100644 --- a/drivers/unix/dir_access_unix.h +++ b/drivers/unix/dir_access_unix.h @@ -31,7 +31,7 @@ #ifndef DIR_ACCESS_UNIX_H #define DIR_ACCESS_UNIX_H -#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) +#if defined(UNIX_ENABLED) #include "core/io/dir_access.h" @@ -90,6 +90,6 @@ public: ~DirAccessUnix(); }; -#endif // UNIX_ENABLED || LIBC_FILEIO_ENABLED +#endif // UNIX_ENABLED #endif // DIR_ACCESS_UNIX_H diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index 388ad479b9..0b80fb1491 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -30,24 +30,20 @@ #include "file_access_unix.h" -#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) +#if defined(UNIX_ENABLED) #include "core/os/os.h" #include "core/string/print_string.h" +#include <errno.h> +#include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> -#include <errno.h> - #if defined(UNIX_ENABLED) #include <unistd.h> #endif -#ifndef ANDROID_ENABLED -#include <sys/statvfs.h> -#endif - #ifdef MSVC #define S_ISREG(m) ((m)&_S_IFREG) #include <io.h> @@ -56,12 +52,6 @@ #define S_ISREG(m) ((m)&S_IFREG) #endif -#ifndef NO_FCNTL -#include <fcntl.h> -#else -#include <sys/ioctl.h> -#endif - void FileAccessUnix::check_errors() const { ERR_FAIL_COND_MSG(!f, "File must be opened before use."); @@ -70,7 +60,7 @@ void FileAccessUnix::check_errors() const { } } -Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) { +Error FileAccessUnix::open_internal(const String &p_path, int p_mode_flags) { _close(); path_src = p_path; @@ -96,7 +86,7 @@ Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) { backend (unix-compatible mostly) supports utf8 encoding */ //printf("opening %s as %s\n", p_path.utf8().get_data(), path.utf8().get_data()); - struct stat st; + struct stat st = {}; int err = stat(path.utf8().get_data(), &st); if (!err) { switch (st.st_mode & S_IFMT) { @@ -131,13 +121,8 @@ Error FileAccessUnix::_open(const String &p_path, int p_mode_flags) { int fd = fileno(f); if (fd != -1) { -#if defined(NO_FCNTL) - unsigned long par = 0; - ioctl(fd, FIOCLEX, &par); -#else int opts = fcntl(fd, F_GETFD); fcntl(fd, F_SETFD, opts | FD_CLOEXEC); -#endif } last_error = OK; @@ -267,7 +252,7 @@ void FileAccessUnix::store_buffer(const uint8_t *p_src, uint64_t p_length) { bool FileAccessUnix::file_exists(const String &p_path) { int err; - struct stat st; + struct stat st = {}; String filename = fix_path(p_path); // Does the name exist at all? @@ -299,7 +284,7 @@ bool FileAccessUnix::file_exists(const String &p_path) { uint64_t FileAccessUnix::_get_modified_time(const String &p_file) { String file = fix_path(p_file); - struct stat flags; + struct stat flags = {}; int err = stat(file.utf8().get_data(), &flags); if (!err) { @@ -312,7 +297,7 @@ uint64_t FileAccessUnix::_get_modified_time(const String &p_file) { uint32_t FileAccessUnix::_get_unix_permissions(const String &p_file) { String file = fix_path(p_file); - struct stat flags; + struct stat flags = {}; int err = stat(file.utf8().get_data(), &flags); if (!err) { @@ -339,4 +324,4 @@ FileAccessUnix::~FileAccessUnix() { _close(); } -#endif // UNIX_ENABLED || LIBC_FILEIO_ENABLED +#endif // UNIX_ENABLED diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index 297c34e454..8c9afe75e7 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -36,7 +36,7 @@ #include <stdio.h> -#if defined(UNIX_ENABLED) || defined(LIBC_FILEIO_ENABLED) +#if defined(UNIX_ENABLED) typedef void (*CloseNotificationFunc)(const String &p_file, int p_flags); @@ -54,7 +54,7 @@ class FileAccessUnix : public FileAccess { public: static CloseNotificationFunc close_notification_func; - virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file + virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file virtual bool is_open() const override; ///< true when file is open virtual String get_path() const override; /// returns the path for the current open file @@ -86,6 +86,6 @@ public: virtual ~FileAccessUnix(); }; -#endif // UNIX_ENABLED || LIBC_FILEIO_ENABLED +#endif // UNIX_ENABLED #endif // FILE_ACCESS_UNIX_H diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_posix.cpp index 86adf33d62..c6b327eeee 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_posix.cpp @@ -30,32 +30,30 @@ #include "net_socket_posix.h" +// Some proprietary Unix-derived platforms don't expose Unix sockets +// so this allows skipping this file to reimplement this API differently. #ifndef UNIX_SOCKET_UNAVAILABLE + #if defined(UNIX_ENABLED) #include <errno.h> +#include <fcntl.h> #include <netdb.h> +#include <netinet/in.h> +#include <netinet/tcp.h> #include <poll.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/ioctl.h> +#include <sys/socket.h> #include <sys/types.h> #include <unistd.h> -#ifndef NO_FCNTL -#include <fcntl.h> -#else -#include <sys/ioctl.h> -#endif -#include <netinet/in.h> -#include <sys/socket.h> #ifdef WEB_ENABLED #include <arpa/inet.h> #endif -#include <netinet/tcp.h> - // BSD calls this flag IPV6_JOIN_GROUP #if !defined(IPV6_ADD_MEMBERSHIP) && defined(IPV6_JOIN_GROUP) #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP @@ -93,7 +91,7 @@ #define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15) #endif -#endif +#endif // UNIX_ENABLED size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) { memset(p_addr, 0, sizeof(struct sockaddr_storage)); @@ -181,8 +179,8 @@ NetSocketPosix::~NetSocketPosix() { close(); } -// Silent a warning reported in #27594 - +// Silence a warning reported in GH-27594. +// EAGAIN and EWOULDBLOCK have the same value on most platforms, but it's not guaranteed. #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wlogical-op" @@ -309,14 +307,9 @@ void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_ void NetSocketPosix::_set_close_exec_enabled(bool p_enabled) { #ifndef WINDOWS_ENABLED // Enable close on exec to avoid sharing with subprocesses. Off by default on Windows. -#if defined(NO_FCNTL) - unsigned long par = p_enabled ? 1 : 0; - SOCK_IOCTL(_sock, FIOCLEX, &par); -#else int opts = fcntl(_sock, F_GETFD); fcntl(_sock, F_SETFD, opts | FD_CLOEXEC); #endif -#endif } Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { @@ -658,7 +651,7 @@ void NetSocketPosix::set_blocking_enabled(bool p_enabled) { ERR_FAIL_COND(!is_open()); int ret = 0; -#if defined(WINDOWS_ENABLED) || defined(NO_FCNTL) +#if defined(WINDOWS_ENABLED) unsigned long par = p_enabled ? 0 : 1; ret = SOCK_IOCTL(_sock, FIONBIO, &par); #else @@ -781,4 +774,5 @@ Error NetSocketPosix::join_multicast_group(const IPAddress &p_multi_address, Str Error NetSocketPosix::leave_multicast_group(const IPAddress &p_multi_address, String p_if_name) { return _change_multicast_group(p_multi_address, p_if_name, false); } -#endif + +#endif // UNIX_SOCKET_UNAVAILABLE diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index ab298a0e49..fc06291a3a 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -51,7 +51,6 @@ #include <sys/sysctl.h> #endif -#include <assert.h> #include <dlfcn.h> #include <errno.h> #include <poll.h> @@ -104,10 +103,6 @@ static void _setup_clock() { } #endif -void OS_Unix::debug_break() { - assert(false); -} - static void handle_interrupt(int sig) { if (!EngineDebugger::is_active()) { return; @@ -131,9 +126,7 @@ int OS_Unix::unix_initialize_audio(int p_audio_driver) { } void OS_Unix::initialize_core() { -#if !defined(NO_THREADS) init_thread_posix(); -#endif FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES); FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA); @@ -142,10 +135,8 @@ void OS_Unix::initialize_core() { DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA); DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM); -#ifndef NO_NETWORK NetSocketPosix::make_default(); IPUnix::make_default(); -#endif _setup_clock(); } @@ -154,6 +145,10 @@ void OS_Unix::finalize_core() { NetSocketPosix::cleanup(); } +Vector<String> OS_Unix::get_video_adapter_driver_info() const { + return Vector<String>(); +} + String OS_Unix::get_stdin_string(bool p_block) { if (p_block) { char buff[1024]; @@ -175,6 +170,7 @@ Error OS_Unix::get_entropy(uint8_t *r_buffer, int p_bytes) { left -= chunk; ofs += chunk; } while (left > 0); +// Define this yourself if you don't want to fall back to /dev/urandom. #elif !defined(NO_URANDOM) int r = open("/dev/urandom", O_RDONLY); ERR_FAIL_COND_V(r < 0, FAILED); @@ -508,10 +504,6 @@ bool OS_Unix::set_environment(const String &p_var, const String &p_value) const return setenv(p_var.utf8().get_data(), p_value.utf8().get_data(), /* overwrite: */ true) == 0; } -int OS_Unix::get_processor_count() const { - return sysconf(_SC_NPROCESSORS_CONF); -} - String OS_Unix::get_user_data_dir() const { String appname = get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name")); if (!appname.is_empty()) { diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h index 8ef650f28b..ce06a52a95 100644 --- a/drivers/unix/os_unix.h +++ b/drivers/unix/os_unix.h @@ -51,9 +51,11 @@ protected: public: OS_Unix(); + virtual Vector<String> get_video_adapter_driver_info() const override; + virtual String get_stdin_string(bool p_block) override; - virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override; // Should return cryptographycally-safe random bytes. + virtual Error get_entropy(uint8_t *r_buffer, int p_bytes) override; virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr) override; virtual Error close_dynamic_library(void *p_library_handle) override; @@ -84,9 +86,6 @@ public: virtual bool set_environment(const String &p_var, const String &p_value) const override; virtual String get_locale() const override; - virtual int get_processor_count() const override; - - virtual void debug_break() override; virtual void initialize_debugging() override; virtual String get_executable_path() const override; diff --git a/drivers/unix/thread_posix.cpp b/drivers/unix/thread_posix.cpp index cb5f261e6e..5154feb478 100644 --- a/drivers/unix/thread_posix.cpp +++ b/drivers/unix/thread_posix.cpp @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#if (defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED)) && !defined(NO_THREADS) +#if defined(UNIX_ENABLED) #include "thread_posix.h" @@ -70,7 +70,7 @@ static Error set_name(const String &p_name) { } void init_thread_posix() { - Thread::_set_platform_funcs(&set_name, nullptr); + Thread::_set_platform_functions({ .set_name = set_name }); } -#endif +#endif // UNIX_ENABLED diff --git a/drivers/unix/thread_posix.h b/drivers/unix/thread_posix.h index 672adcba72..87e42b3870 100644 --- a/drivers/unix/thread_posix.h +++ b/drivers/unix/thread_posix.h @@ -31,8 +31,6 @@ #ifndef THREAD_POSIX_H #define THREAD_POSIX_H -#if !defined(NO_THREADS) void init_thread_posix(); -#endif #endif // THREAD_POSIX_H diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 73ae108961..de29363556 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -1655,9 +1655,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T image_create_info.pNext = nullptr; image_create_info.flags = 0; -#ifndef _MSC_VER -#warning TODO check for support via RenderingDevice to enable on mobile when possible -#endif + // TODO: Check for support via RenderingDevice to enable on mobile when possible. #ifndef ANDROID_ENABLED @@ -4152,8 +4150,8 @@ RID RenderingDeviceVulkan::framebuffer_create_multipass(const Vector<RID> &p_tex size.height = texture->height; size_set = true; } else if (texture->usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) { - // If this is not the first attachement we assume this is used as the VRS attachment. - // In this case this texture will be 1/16th the size of the color attachement. + // If this is not the first attachment we assume this is used as the VRS attachment. + // In this case this texture will be 1/16th the size of the color attachment. // So we skip the size check. } else { ERR_FAIL_COND_V_MSG((uint32_t)size.width != texture->width || (uint32_t)size.height != texture->height, RID(), @@ -4828,7 +4826,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve for (uint32_t j = 0; j < binding_count; j++) { const SpvReflectDescriptorBinding &binding = *bindings[j]; - RenderingDeviceVulkanShaderBinaryDataBinding info; + RenderingDeviceVulkanShaderBinaryDataBinding info{}; bool need_array_dimensions = false; bool need_block_size = false; @@ -4979,7 +4977,7 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve for (uint32_t j = 0; j < sc_count; j++) { int32_t existing = -1; - RenderingDeviceVulkanShaderBinarySpecializationConstant sconst; + RenderingDeviceVulkanShaderBinarySpecializationConstant sconst{}; SpvReflectSpecializationConstant *spc = spec_constants[j]; sconst.constant_id = spc->constant_id; @@ -5172,9 +5170,9 @@ Vector<uint8_t> RenderingDeviceVulkan::shader_compile_binary_from_spirv(const Ve uint32_t offset = 0; uint8_t *binptr = ret.ptrw(); binptr[0] = 'G'; - binptr[1] = 'V'; + binptr[1] = 'S'; binptr[2] = 'B'; - binptr[3] = 'D'; // Godot vulkan binary data. + binptr[3] = 'D'; // Godot Shader Binary Data. offset += 4; encode_uint32(SHADER_BINARY_VERSION, binptr + offset); offset += sizeof(uint32_t); @@ -5235,7 +5233,7 @@ RID RenderingDeviceVulkan::shader_create_from_bytecode(const Vector<uint8_t> &p_ uint32_t read_offset = 0; // Consistency check. ERR_FAIL_COND_V(binsize < sizeof(uint32_t) * 3 + sizeof(RenderingDeviceVulkanShaderBinaryData), RID()); - ERR_FAIL_COND_V(binptr[0] != 'G' || binptr[1] != 'V' || binptr[2] != 'B' || binptr[3] != 'D', RID()); + ERR_FAIL_COND_V(binptr[0] != 'G' || binptr[1] != 'S' || binptr[2] != 'B' || binptr[3] != 'D', RID()); uint32_t bin_version = decode_uint32(binptr + 4); ERR_FAIL_COND_V(bin_version != SHADER_BINARY_VERSION, RID()); @@ -6561,7 +6559,7 @@ RID RenderingDeviceVulkan::render_pipeline_create(RID p_shader, FramebufferForma ERR_FAIL_INDEX_V(p_rasterization_state.cull_mode, 3, RID()); rasterization_state_create_info.cullMode = cull_mode[p_rasterization_state.cull_mode]; rasterization_state_create_info.frontFace = (p_rasterization_state.front_face == POLYGON_FRONT_FACE_CLOCKWISE ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE); - rasterization_state_create_info.depthBiasEnable = p_rasterization_state.depth_bias_enable; + rasterization_state_create_info.depthBiasEnable = p_rasterization_state.depth_bias_enabled; rasterization_state_create_info.depthBiasConstantFactor = p_rasterization_state.depth_bias_constant_factor; rasterization_state_create_info.depthBiasClamp = p_rasterization_state.depth_bias_clamp; rasterization_state_create_info.depthBiasSlopeFactor = p_rasterization_state.depth_bias_slope_factor; @@ -7262,12 +7260,12 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff return OK; } -void RenderingDeviceVulkan::_draw_list_insert_clear_region(DrawList *draw_list, Framebuffer *framebuffer, Point2i viewport_offset, Point2i viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil) { +void RenderingDeviceVulkan::_draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil) { Vector<VkClearAttachment> clear_attachments; int color_index = 0; int texture_index = 0; - for (int i = 0; i < framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); + for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { + Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); if (!texture) { texture_index++; @@ -7300,12 +7298,12 @@ void RenderingDeviceVulkan::_draw_list_insert_clear_region(DrawList *draw_list, VkClearRect cr; cr.baseArrayLayer = 0; cr.layerCount = 1; - cr.rect.offset.x = viewport_offset.x; - cr.rect.offset.y = viewport_offset.y; - cr.rect.extent.width = viewport_size.width; - cr.rect.extent.height = viewport_size.height; + cr.rect.offset.x = p_viewport_offset.x; + cr.rect.offset.y = p_viewport_offset.y; + cr.rect.extent.width = p_viewport_size.width; + cr.rect.extent.height = p_viewport_size.height; - vkCmdClearAttachments(draw_list->command_buffer, clear_attachments.size(), clear_attachments.ptr(), 1, &cr); + vkCmdClearAttachments(p_draw_list->command_buffer, clear_attachments.size(), clear_attachments.ptr(), 1, &cr); } RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) { @@ -9367,10 +9365,10 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de // NOTE: If adding new project settings here, also duplicate their definition in // rendering_server.cpp for headless doctool. - staging_buffer_block_size = GLOBAL_DEF("rendering/vulkan/staging_buffer/block_size_kb", 256); + staging_buffer_block_size = GLOBAL_DEF("rendering/rendering_device/staging_buffer/block_size_kb", 256); staging_buffer_block_size = MAX(4u, staging_buffer_block_size); staging_buffer_block_size *= 1024; // Kb -> bytes. - staging_buffer_max_size = GLOBAL_DEF("rendering/vulkan/staging_buffer/max_size_mb", 128); + staging_buffer_max_size = GLOBAL_DEF("rendering/rendering_device/staging_buffer/max_size_mb", 128); staging_buffer_max_size = MAX(1u, staging_buffer_max_size); staging_buffer_max_size *= 1024 * 1024; @@ -9378,7 +9376,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de // Validate enough blocks. staging_buffer_max_size = staging_buffer_block_size * 4; } - texture_upload_region_size_px = GLOBAL_DEF("rendering/vulkan/staging_buffer/texture_upload_region_size_px", 64); + texture_upload_region_size_px = GLOBAL_DEF("rendering/rendering_device/staging_buffer/texture_upload_region_size_px", 64); texture_upload_region_size_px = nearest_power_of_2_templated(texture_upload_region_size_px); frames_drawn = frame_count; // Start from frame count, so everything else is immediately old. @@ -9393,7 +9391,7 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de ERR_CONTINUE(err != OK); } - max_descriptors_per_pool = GLOBAL_DEF("rendering/vulkan/descriptor_pools/max_descriptors_per_pool", 64); + max_descriptors_per_pool = GLOBAL_DEF("rendering/rendering_device/vulkan/max_descriptors_per_pool", 64); // Check to make sure DescriptorPoolKey is good. static_assert(sizeof(uint64_t) * 3 >= UNIFORM_TYPE_MAX * sizeof(uint16_t)); @@ -9688,6 +9686,10 @@ uint64_t RenderingDeviceVulkan::limit_get(Limit p_limit) const { return limits.maxComputeWorkGroupSize[1]; case LIMIT_MAX_COMPUTE_WORKGROUP_SIZE_Z: return limits.maxComputeWorkGroupSize[2]; + case LIMIT_MAX_VIEWPORT_DIMENSIONS_X: + return limits.maxViewportDimensions[0]; + case LIMIT_MAX_VIEWPORT_DIMENSIONS_Y: + return limits.maxViewportDimensions[1]; case LIMIT_SUBGROUP_SIZE: { VulkanContext::SubgroupCapabilities subgroup_capabilities = context->get_subgroup_capabilities(); return subgroup_capabilities.size; diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index abec1b0e1b..7c75e9bb2e 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -905,7 +905,7 @@ class RenderingDeviceVulkan : public RenderingDevice { bool draw_list_unbind_color_textures = false; bool draw_list_unbind_depth_textures = false; - void _draw_list_insert_clear_region(DrawList *draw_list, Framebuffer *framebuffer, Point2i viewport_offset, Point2i viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); + void _draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass, uint32_t *r_subpass_count); Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures); _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 99ef57abae..9ee5a67471 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -48,15 +48,119 @@ VulkanHooks *VulkanContext::vulkan_hooks = nullptr; -VkResult VulkanContext::vkCreateRenderPass2KHR(VkDevice device, const VkRenderPassCreateInfo2 *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass) { - if (fpCreateRenderPass2KHR == nullptr) { - fpCreateRenderPass2KHR = (PFN_vkCreateRenderPass2KHR)vkGetInstanceProcAddr(inst, "vkCreateRenderPass2KHR"); +Vector<VkAttachmentReference> VulkanContext::_convert_VkAttachmentReference2(uint32_t p_count, const VkAttachmentReference2 *p_refs) { + Vector<VkAttachmentReference> att_refs; + + if (p_refs != nullptr) { + for (uint32_t i = 0; i < p_count; i++) { + // We lose aspectMask in this conversion but we don't use it currently. + + VkAttachmentReference ref = { + p_refs[i].attachment, /* attachment */ + p_refs[i].layout /* layout */ + }; + + att_refs.push_back(ref); + } } - if (fpCreateRenderPass2KHR == nullptr) { - return VK_ERROR_EXTENSION_NOT_PRESENT; + return att_refs; +} + +VkResult VulkanContext::vkCreateRenderPass2KHR(VkDevice p_device, const VkRenderPassCreateInfo2 *p_create_info, const VkAllocationCallbacks *p_allocator, VkRenderPass *p_render_pass) { + if (has_renderpass2_ext) { + if (fpCreateRenderPass2KHR == nullptr) { + fpCreateRenderPass2KHR = (PFN_vkCreateRenderPass2KHR)vkGetDeviceProcAddr(p_device, "vkCreateRenderPass2KHR"); + } + + if (fpCreateRenderPass2KHR == nullptr) { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } else { + return (fpCreateRenderPass2KHR)(p_device, p_create_info, p_allocator, p_render_pass); + } } else { - return (fpCreateRenderPass2KHR)(device, pCreateInfo, pAllocator, pRenderPass); + // need to fall back on vkCreateRenderPass + + const void *next = p_create_info->pNext; // ATM we only support multiview which should work if supported. + + Vector<VkAttachmentDescription> attachments; + for (uint32_t i = 0; i < p_create_info->attachmentCount; i++) { + // Basically the old layout just misses type and next. + VkAttachmentDescription att = { + p_create_info->pAttachments[i].flags, /* flags */ + p_create_info->pAttachments[i].format, /* format */ + p_create_info->pAttachments[i].samples, /* samples */ + p_create_info->pAttachments[i].loadOp, /* loadOp */ + p_create_info->pAttachments[i].storeOp, /* storeOp */ + p_create_info->pAttachments[i].stencilLoadOp, /* stencilLoadOp */ + p_create_info->pAttachments[i].stencilStoreOp, /* stencilStoreOp */ + p_create_info->pAttachments[i].initialLayout, /* initialLayout */ + p_create_info->pAttachments[i].finalLayout /* finalLayout */ + }; + + attachments.push_back(att); + } + + Vector<VkSubpassDescription> subpasses; + for (uint32_t i = 0; i < p_create_info->subpassCount; i++) { + // Here we need to do more, again it's just stripping out type and next + // but we have VkAttachmentReference2 to convert to VkAttachmentReference. + // Also viewmask is not supported but we don't use it outside of multiview. + + Vector<VkAttachmentReference> input_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].inputAttachmentCount, p_create_info->pSubpasses[i].pInputAttachments); + Vector<VkAttachmentReference> color_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pColorAttachments); + Vector<VkAttachmentReference> resolve_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pResolveAttachments); + Vector<VkAttachmentReference> depth_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pDepthStencilAttachment); + + VkSubpassDescription subpass = { + p_create_info->pSubpasses[i].flags, /* flags */ + p_create_info->pSubpasses[i].pipelineBindPoint, /* pipelineBindPoint */ + p_create_info->pSubpasses[i].inputAttachmentCount, /* inputAttachmentCount */ + input_attachments.size() == 0 ? nullptr : input_attachments.ptr(), /* pInputAttachments */ + p_create_info->pSubpasses[i].colorAttachmentCount, /* colorAttachmentCount */ + color_attachments.size() == 0 ? nullptr : color_attachments.ptr(), /* pColorAttachments */ + resolve_attachments.size() == 0 ? nullptr : resolve_attachments.ptr(), /* pResolveAttachments */ + depth_attachments.size() == 0 ? nullptr : depth_attachments.ptr(), /* pDepthStencilAttachment */ + p_create_info->pSubpasses[i].preserveAttachmentCount, /* preserveAttachmentCount */ + p_create_info->pSubpasses[i].pPreserveAttachments /* pPreserveAttachments */ + }; + + subpasses.push_back(subpass); + } + + Vector<VkSubpassDependency> dependencies; + for (uint32_t i = 0; i < p_create_info->dependencyCount; i++) { + // We lose viewOffset here but again I don't believe we use this anywhere. + VkSubpassDependency dep = { + p_create_info->pDependencies[i].srcSubpass, /* srcSubpass */ + p_create_info->pDependencies[i].dstSubpass, /* dstSubpass */ + p_create_info->pDependencies[i].srcStageMask, /* srcStageMask */ + p_create_info->pDependencies[i].dstStageMask, /* dstStageMask */ + p_create_info->pDependencies[i].srcAccessMask, /* srcAccessMask */ + p_create_info->pDependencies[i].dstAccessMask, /* dstAccessMask */ + p_create_info->pDependencies[i].dependencyFlags, /* dependencyFlags */ + }; + + dependencies.push_back(dep); + } + + // CorrelatedViewMask is not supported in vkCreateRenderPass but we + // currently only use this for multiview. + // We'll need to look into this. + + VkRenderPassCreateInfo create_info = { + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, /* sType */ + next, /* pNext*/ + p_create_info->flags, /* flags */ + (uint32_t)attachments.size(), /* attachmentCount */ + attachments.ptr(), /* pAttachments */ + (uint32_t)subpasses.size(), /* subpassCount */ + subpasses.ptr(), /* pSubpasses */ + (uint32_t)dependencies.size(), /* */ + dependencies.ptr(), /* */ + }; + + return vkCreateRenderPass(device, &create_info, p_allocator, p_render_pass); } } @@ -85,19 +189,6 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( return VK_FALSE; } - // Workaround for Vulkan-Loader usability bug: https://github.com/KhronosGroup/Vulkan-Loader/issues/262. - if (strstr(pCallbackData->pMessage, "wrong ELF class: ELFCLASS32") != nullptr) { - return VK_FALSE; - } - -#ifdef WINDOWS_ENABLED - // Some software installs Vulkan overlays in Windows registry and never cleans them up on uninstall. - // So we get spammy error level messages from the loader about those - make them verbose instead. - if (strstr(pCallbackData->pMessage, "loader_get_json: Failed to open JSON file") != nullptr) { - messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; - } -#endif - if (pCallbackData->pMessageIdName && strstr(pCallbackData->pMessageIdName, "UNASSIGNED-CoreValidation-DrawState-ClearCmdBeforeDraw") != nullptr) { return VK_FALSE; } @@ -320,6 +411,16 @@ Error VulkanContext::_initialize_extensions() { VkBool32 platformSurfaceExtFound = 0; memset(extension_names, 0, sizeof(extension_names)); + // Only enable debug utils in verbose mode or DEV_ENABLED. + // End users would get spammed with messages of varying verbosity due to the + // mess that thirdparty layers/extensions and drivers seem to leave in their + // wake, making the Windows registry a bottomless pit of broken layer JSON. +#ifdef DEV_ENABLED + bool want_debug_utils = true; +#else + bool want_debug_utils = OS::get_singleton()->is_stdout_verbose(); +#endif + VkResult err = vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, nullptr); ERR_FAIL_COND_V(err != VK_SUCCESS && err != VK_INCOMPLETE, ERR_CANT_CREATE); @@ -347,8 +448,10 @@ Error VulkanContext::_initialize_extensions() { } } if (!strcmp(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, instance_extensions[i].extensionName)) { - extension_names[enabled_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; - enabled_debug_utils = true; + if (want_debug_utils) { + extension_names[enabled_extension_count++] = VK_EXT_DEBUG_UTILS_EXTENSION_NAME; + enabled_debug_utils = true; + } } if (!strcmp(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, instance_extensions[i].extensionName)) { extension_names[enabled_extension_count++] = VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME; @@ -608,15 +711,18 @@ Error VulkanContext::_check_capabilities() { device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2KHR"); } if (device_properties_func != nullptr) { - VkPhysicalDeviceFragmentShadingRatePropertiesKHR vrsProperties; - VkPhysicalDeviceMultiviewProperties multiviewProperties; - VkPhysicalDeviceSubgroupProperties subgroupProperties; - VkPhysicalDeviceProperties2 physicalDeviceProperties; + VkPhysicalDeviceFragmentShadingRatePropertiesKHR vrsProperties{}; + VkPhysicalDeviceMultiviewProperties multiviewProperties{}; + VkPhysicalDeviceSubgroupProperties subgroupProperties{}; + VkPhysicalDeviceProperties2 physicalDeviceProperties{}; void *nextptr = nullptr; - subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; - subgroupProperties.pNext = nextptr; - nextptr = &subgroupProperties; + if (!(vulkan_major == 1 && vulkan_minor == 0)) { + subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; + subgroupProperties.pNext = nextptr; + + nextptr = &subgroupProperties; + } if (multiview_capabilities.is_supported) { multiviewProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; @@ -1061,6 +1167,7 @@ Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { extension_names[enabled_extension_count++] = VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME; } if (!strcmp(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, device_extensions[i].extensionName)) { + has_renderpass2_ext = true; extension_names[enabled_extension_count++] = VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME; } if (enabled_extension_count >= MAX_EXTENSIONS) { @@ -1703,18 +1810,22 @@ Error VulkanContext::_update_swap_chain(Window *window) { preTransform = surfCapabilities.currentTransform; } - // Find a supported composite alpha mode - one of these is guaranteed to be set. VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = { - VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, - VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, - VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, - }; - for (uint32_t i = 0; i < ARRAY_SIZE(compositeAlphaFlags); i++) { - if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i]) { - compositeAlpha = compositeAlphaFlags[i]; - break; + + if (OS::get_singleton()->is_layered_allowed() || !(surfCapabilities.supportedCompositeAlpha & compositeAlpha)) { + // Find a supported composite alpha mode - one of these is guaranteed to be set. + VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = { + VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + }; + + for (uint32_t i = 0; i < ARRAY_SIZE(compositeAlphaFlags); i++) { + if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i]) { + compositeAlpha = compositeAlphaFlags[i]; + break; + } } } @@ -2261,8 +2372,6 @@ Error VulkanContext::swap_buffers() { } } #endif - static int total_frames = 0; - total_frames++; // print_line("current buffer: " + itos(current_buffer)); err = fpQueuePresentKHR(present_queue, &present); diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index 9889cf336b..b47aec1de1 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -188,6 +188,7 @@ private: uint32_t enabled_extension_count = 0; const char *extension_names[MAX_EXTENSIONS]; bool enabled_debug_utils = false; + bool has_renderpass2_ext = false; /** * True if VK_EXT_debug_report extension is used. VK_EXT_debug_report is deprecated but it is @@ -257,6 +258,8 @@ private: Error _create_swap_chain(); Error _create_semaphores(); + Vector<VkAttachmentReference> _convert_VkAttachmentReference2(uint32_t p_count, const VkAttachmentReference2 *p_refs); + protected: virtual const char *_get_platform_surface_extension() const = 0; @@ -270,7 +273,7 @@ protected: public: // Extension calls. - VkResult vkCreateRenderPass2KHR(VkDevice device, const VkRenderPassCreateInfo2 *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkRenderPass *pRenderPass); + VkResult vkCreateRenderPass2KHR(VkDevice p_device, const VkRenderPassCreateInfo2 *p_create_info, const VkAllocationCallbacks *p_allocator, VkRenderPass *p_render_pass); uint32_t get_vulkan_major() const { return vulkan_major; }; uint32_t get_vulkan_minor() const { return vulkan_minor; }; diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index fb90b776cf..d6636606d2 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -127,6 +127,11 @@ static bool default_capture_device_changed = false; #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #endif +#if defined(__GNUC__) +// Workaround GCC warning from -Wcast-function-type. +#define GetProcAddress (void *)GetProcAddress +#endif + class CMMNotificationClient : public IMMNotificationClient { LONG _cRef = 1; IMMDeviceEnumerator *_pEnumerator = nullptr; @@ -201,6 +206,20 @@ public: static CMMNotificationClient notif_client; +typedef const char *(CDECL *PWineGetVersionPtr)(void); + +bool AudioDriverWASAPI::is_running_on_wine() { + HMODULE nt_lib = LoadLibraryW(L"ntdll.dll"); + if (!nt_lib) { + return false; + } + + PWineGetVersionPtr wine_get_version = (PWineGetVersionPtr)GetProcAddress(nt_lib, "wine_get_version"); + FreeLibrary(nt_lib); + + return (bool)wine_get_version; +} + Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_capture, bool reinit) { WAVEFORMATEX *pwfex; IMMDeviceEnumerator *enumerator = nullptr; @@ -285,6 +304,10 @@ Error AudioDriverWASAPI::audio_device_init(AudioDeviceWASAPI *p_device, bool p_c } using_audio_client_3 = !p_capture; // IID_IAudioClient3 is only used for adjustable output latency (not input) + if (using_audio_client_3 && is_running_on_wine()) { + using_audio_client_3 = false; + print_verbose("WASAPI: Wine detected, falling back to IAudioClient interface"); + } if (using_audio_client_3) { hr = device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void **)&p_device->audio_client); if (hr != S_OK) { diff --git a/drivers/wasapi/audio_driver_wasapi.h b/drivers/wasapi/audio_driver_wasapi.h index c30a54c042..e9f2794e97 100644 --- a/drivers/wasapi/audio_driver_wasapi.h +++ b/drivers/wasapi/audio_driver_wasapi.h @@ -79,6 +79,8 @@ class AudioDriverWASAPI : public AudioDriver { SafeFlag exit_thread; + static bool is_running_on_wine(); + static _FORCE_INLINE_ void write_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i, int32_t sample); static _FORCE_INLINE_ int32_t read_sample(WORD format_tag, int bits_per_sample, BYTE *buffer, int i); static void thread_func(void *p_udata); diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp index 11fd29c8f5..c085ba372b 100644 --- a/drivers/windows/dir_access_windows.cpp +++ b/drivers/windows/dir_access_windows.cpp @@ -160,7 +160,7 @@ Error DirAccessWindows::make_dir(String p_dir) { p_dir = current_dir.path_join(p_dir); } - p_dir = p_dir.replace("/", "\\"); + p_dir = p_dir.simplify_path().replace("/", "\\"); bool success; int err; @@ -200,9 +200,9 @@ String DirAccessWindows::get_current_dir(bool p_include_drive) const { return current_dir; } else { if (_get_root_string().is_empty()) { - int p = current_dir.find(":"); - if (p != -1) { - return current_dir.substr(p + 1); + int pos = current_dir.find(":"); + if (pos != -1) { + return current_dir.substr(pos + 1); } } return current_dir; @@ -309,39 +309,13 @@ Error DirAccessWindows::remove(String p_path) { } } -/* - -FileType DirAccessWindows::get_file_type(const String& p_file) const { - WCHAR real_current_dir_name[2048]; - GetCurrentDirectoryW(2048, real_current_dir_name); - String prev_dir = Strong::utf16((const char16_t *)real_current_dir_name); - - bool worked = SetCurrentDirectoryW((LPCWSTR)(current_dir.utf16().get_data())); - - DWORD attr; - if (worked) { - WIN32_FILE_ATTRIBUTE_DATA fileInfo; - attr = GetFileAttributesExW((LPCWSTR)(p_file.utf16().get_data()), GetFileExInfoStandard, &fileInfo); - } - - SetCurrentDirectoryW((LPCWSTR)(prev_dir.utf16().get_data())); - - if (!worked) { - return FILE_TYPE_NONE; - } - - return (attr & FILE_ATTRIBUTE_DIRECTORY) ? FILE_TYPE_ -} - -*/ - uint64_t DirAccessWindows::get_space_left() { uint64_t bytes = 0; if (!GetDiskFreeSpaceEx(nullptr, (PULARGE_INTEGER)&bytes, nullptr, nullptr)) { return 0; } - //this is either 0 or a value in bytes. + // This is either 0 or a value in bytes. return bytes; } diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 1a66d19373..4e8b327ffe 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -58,7 +58,7 @@ void FileAccessWindows::check_errors() const { } } -Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { +Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) { _close(); path_src = p_path; @@ -95,8 +95,8 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { // platforms). if (p_mode_flags == READ) { WIN32_FIND_DATAW d; - HANDLE f = FindFirstFileW((LPCWSTR)(path.utf16().get_data()), &d); - if (f != INVALID_HANDLE_VALUE) { + HANDLE fnd = FindFirstFileW((LPCWSTR)(path.utf16().get_data()), &d); + if (fnd != INVALID_HANDLE_VALUE) { String fname = String::utf16((const char16_t *)(d.cFileName)); if (!fname.is_empty()) { String base_file = path.get_file(); @@ -104,7 +104,7 @@ Error FileAccessWindows::_open(const String &p_path, int p_mode_flags) { WARN_PRINT("Case mismatch opening requested file '" + base_file + "', stored as '" + fname + "' in the filesystem. This file will not open when exported to other case-sensitive platforms."); } } - FindClose(f); + FindClose(fnd); } } #endif diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h index 8629bb936b..d84c400775 100644 --- a/drivers/windows/file_access_windows.h +++ b/drivers/windows/file_access_windows.h @@ -51,7 +51,7 @@ class FileAccessWindows : public FileAccess { void _close(); public: - virtual Error _open(const String &p_path, int p_mode_flags) override; ///< open a file + virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file virtual bool is_open() const override; ///< true when file is open virtual String get_path() const override; /// returns the path for the current open file |