diff options
52 files changed, 904 insertions, 402 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 33ec541bc7..e08c8bf9de 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -31,7 +31,7 @@ doc_classes/* @godotengine/documentation /modules/csg/ @BastiaanOlij /modules/enet/ @godotengine/network /modules/gdnative/*arvr/ @BastiaanOlij -/modules/gdscript/ @vnen @bojidar-bg +/modules/gdscript/ @vnen /modules/mbedtls/ @godotengine/network /modules/mobile_vr/ @BastiaanOlij /modules/mono/ @neikeq diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml index b170889cb3..f708952f75 100644 --- a/.github/workflows/linux_builds.yml +++ b/.github/workflows/linux_builds.yml @@ -138,12 +138,6 @@ jobs: run: | ./bin/godot.linuxbsd.tools.64s --test - - uses: actions/upload-artifact@v2 - with: - name: ${{ github.job }} - path: bin/* - retention-days: 14 - linux-template-mono: runs-on: "ubuntu-20.04" name: Template w/ Mono (target=release, tools=no) @@ -197,7 +191,7 @@ jobs: env: SCONS_CACHE: ${{github.workspace}}/.scons_cache/ run: | - scons target=release tools=no module_mono_enabled=yes mono_glue=no + scons target=release tools=no module_mono_enabled=yes mono_glue=no debug_symbols=no ls -l bin/ - uses: actions/upload-artifact@v2 diff --git a/.github/workflows/macos_builds.yml b/.github/workflows/macos_builds.yml index 6a684872f2..5f9ad24ac7 100644 --- a/.github/workflows/macos_builds.yml +++ b/.github/workflows/macos_builds.yml @@ -104,7 +104,7 @@ jobs: env: SCONS_CACHE: ${{github.workspace}}/.scons_cache/ run: | - scons target=release tools=no + scons target=release tools=no debug_symbols=no ls -l bin/ - uses: actions/upload-artifact@v2 diff --git a/.github/workflows/windows_builds.yml b/.github/workflows/windows_builds.yml index 0af4d22b72..71a1b580b1 100644 --- a/.github/workflows/windows_builds.yml +++ b/.github/workflows/windows_builds.yml @@ -110,7 +110,7 @@ jobs: env: SCONS_CACHE: /.scons_cache/ run: | - scons target=release tools=no + scons target=release tools=no debug_symbols=no ls -l bin/ - uses: actions/upload-artifact@v2 diff --git a/doc/classes/ColorRect.xml b/doc/classes/ColorRect.xml index 072759e383..7c0cd981e4 100644 --- a/doc/classes/ColorRect.xml +++ b/doc/classes/ColorRect.xml @@ -4,7 +4,7 @@ Colored rectangle. </brief_description> <description> - Displays a colored rectangle. + Displays a rectangle filled with a solid [member color]. If you need to display the border alone, consider using [ReferenceRect] instead. </description> <tutorials> <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index 9d00ffe233..77bd2c60cc 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -193,16 +193,11 @@ </member> </members> <signals> - <signal name="_begin_node_move"> + <signal name="begin_node_move"> <description> Emitted at the beginning of a GraphNode movement. </description> </signal> - <signal name="_end_node_move"> - <description> - Emitted at the end of a GraphNode movement. - </description> - </signal> <signal name="connection_from_empty"> <argument index="0" name="to" type="StringName"> </argument> @@ -266,6 +261,11 @@ Emitted when a GraphNode is attempted to be duplicated in the GraphEdit. </description> </signal> + <signal name="end_node_move"> + <description> + Emitted at the end of a GraphNode movement. + </description> + </signal> <signal name="node_selected"> <argument index="0" name="node" type="Node"> </argument> diff --git a/doc/classes/ReferenceRect.xml b/doc/classes/ReferenceRect.xml index 4f8f71b3db..b99970199a 100644 --- a/doc/classes/ReferenceRect.xml +++ b/doc/classes/ReferenceRect.xml @@ -4,7 +4,7 @@ Reference frame for GUI. </brief_description> <description> - A rectangle box that displays only a [member border_color] border color around its rectangle. [ReferenceRect] has no fill [Color]. + A rectangle box that displays only a [member border_color] border color around its rectangle. [ReferenceRect] has no fill [Color]. If you need to display a rectangle filled with a solid color, consider using [ColorRect] instead. </description> <tutorials> </tutorials> diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml index 385e31ccc7..4f02cd00dd 100644 --- a/doc/classes/SurfaceTool.xml +++ b/doc/classes/SurfaceTool.xml @@ -38,6 +38,7 @@ </argument> <description> Specifies a [Color] for the next vertex to use. + [b]Note:[/b] The material must have [member BaseMaterial3D.vertex_color_use_as_albedo] enabled for the vertex color to be visible. </description> </method> <method name="add_index"> diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 9fe3f7e6c0..2cbb305153 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -38,7 +38,58 @@ #include "thirdparty/spirv-reflect/spirv_reflect.h" -#define FORCE_FULL_BARRIER +//#define FORCE_FULL_BARRIER + +// Get the Vulkan object information and possible stage access types (bitwise OR'd with incoming values) +RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &stage_mask, VkAccessFlags &access_mask) { + Buffer *buffer = nullptr; + if (vertex_buffer_owner.owns(p_buffer)) { + stage_mask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + access_mask |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; + buffer = vertex_buffer_owner.getornull(p_buffer); + } else if (index_buffer_owner.owns(p_buffer)) { + stage_mask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; + access_mask |= VK_ACCESS_INDEX_READ_BIT; + buffer = index_buffer_owner.getornull(p_buffer); + } else if (uniform_buffer_owner.owns(p_buffer)) { + stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + access_mask |= VK_ACCESS_UNIFORM_READ_BIT; + buffer = uniform_buffer_owner.getornull(p_buffer); + } else if (texture_buffer_owner.owns(p_buffer)) { + stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + access_mask |= VK_ACCESS_SHADER_READ_BIT; + buffer = &texture_buffer_owner.getornull(p_buffer)->buffer; + } else if (storage_buffer_owner.owns(p_buffer)) { + stage_mask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + access_mask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + buffer = storage_buffer_owner.getornull(p_buffer); + } + return buffer; +} + +static void update_external_dependency_for_store(VkSubpassDependency &dependency, bool is_sampled, bool is_storage, bool is_depth) { + // Transitioning from write to read, protect the shaders that may use this next + // Allow for copies/image layout transitions + dependency.dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT; + dependency.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT; + + if (is_sampled) { + dependency.dstStageMask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + dependency.dstAccessMask |= VK_ACCESS_SHADER_READ_BIT; + } else if (is_storage) { + dependency.dstStageMask |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + dependency.dstAccessMask |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + } else { + dependency.dstStageMask |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstAccessMask |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + } + + if (is_depth) { + // Depth resources have addtional stages that may be interested in them + dependency.dstStageMask |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; + dependency.dstAccessMask |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + } +} void RenderingDeviceVulkan::_add_dependency(RID p_id, RID p_depends_on) { if (!dependency_map.has(p_depends_on)) { @@ -1932,7 +1983,7 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T image_memory_barrier.subresourceRange.baseArrayLayer = 0; image_memory_barrier.subresourceRange.layerCount = image_create_info.arrayLayers; - vkCmdPipelineBarrier(frames[frame].setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(frames[frame].setup_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } RID id = texture_owner.make_rid(texture); @@ -2189,7 +2240,7 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } uint32_t mipmap_offset = 0; @@ -2322,7 +2373,7 @@ Error RenderingDeviceVulkan::texture_update(RID p_texture, uint32_t p_layer, con image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } return OK; @@ -2485,6 +2536,9 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t image_memory_barrier.pNext = nullptr; image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + if (tex->usage_flags & TEXTURE_USAGE_STORAGE_BIT) { + image_memory_barrier.dstAccessMask |= VK_ACCESS_SHADER_WRITE_BIT; + } image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; image_memory_barrier.newLayout = tex->layout; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; @@ -2496,7 +2550,7 @@ Vector<uint8_t> RenderingDeviceVulkan::texture_get_data(RID p_texture, uint32_t image_memory_barrier.subresourceRange.baseArrayLayer = p_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } _flush(true); @@ -2672,7 +2726,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, image_memory_barrier.subresourceRange.baseArrayLayer = p_src_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } { //make dst readable @@ -2694,7 +2748,7 @@ Error RenderingDeviceVulkan::texture_copy(RID p_from_texture, RID p_to_texture, image_memory_barrier.subresourceRange.baseArrayLayer = p_src_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } } @@ -2755,7 +2809,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID image_memory_barrier.subresourceRange.baseArrayLayer = src_tex->base_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } { //Dest VkImageMemoryBarrier image_memory_barrier; @@ -2824,7 +2878,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID image_memory_barrier.subresourceRange.baseArrayLayer = src_tex->base_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } { //make dst readable @@ -2846,7 +2900,7 @@ Error RenderingDeviceVulkan::texture_resolve_multisample(RID p_from_texture, RID image_memory_barrier.subresourceRange.baseArrayLayer = dst_tex->base_layer; image_memory_barrier.subresourceRange.layerCount = 1; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } } @@ -2878,16 +2932,22 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, VkCommandBuffer command_buffer = p_sync_with_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer; - VkImageLayout layout = src_tex->layout; + VkImageLayout clear_layout = (src_tex->layout == VK_IMAGE_LAYOUT_GENERAL) ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - if (src_tex->layout != VK_IMAGE_LAYOUT_GENERAL) { //storage may be in general state + // NOTE: Perhaps the valid stages/accesses for a given onwner should be a property of the owner. (Here and places like _get_buffer_from_owner) + const VkPipelineStageFlags valid_texture_stages = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + constexpr VkAccessFlags read_access = VK_ACCESS_SHADER_READ_BIT; + constexpr VkAccessFlags read_write_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + const VkAccessFlags valid_texture_access = (src_tex->usage_flags & TEXTURE_USAGE_STORAGE_BIT) ? read_write_access : read_access; + + { // Barrier from previous access with optional layout change (see clear_layout logic above) VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; - image_memory_barrier.srcAccessMask = 0; + image_memory_barrier.srcAccessMask = valid_texture_access; image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; image_memory_barrier.oldLayout = src_tex->layout; - image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_memory_barrier.oldLayout = clear_layout; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; @@ -2898,8 +2958,7 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, image_memory_barrier.subresourceRange.baseArrayLayer = src_tex->base_layer + p_base_layer; image_memory_barrier.subresourceRange.layerCount = p_layers; - layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, valid_texture_stages, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } VkClearColorValue clear_color; @@ -2915,16 +2974,15 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, range.baseMipLevel = src_tex->base_mipmap + p_base_mipmap; range.levelCount = p_mipmaps; - vkCmdClearColorImage(command_buffer, src_tex->image, layout, &clear_color, 1, &range); - - if (src_tex->layout != VK_IMAGE_LAYOUT_GENERAL) { //storage may be in general state + vkCmdClearColorImage(command_buffer, src_tex->image, clear_layout, &clear_color, 1, &range); + { // Barrier to post clear accesses (changing back the layout if needed) VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; image_memory_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - image_memory_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + image_memory_barrier.dstAccessMask = valid_texture_access; + image_memory_barrier.oldLayout = clear_layout; image_memory_barrier.newLayout = src_tex->layout; image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; @@ -2936,7 +2994,7 @@ Error RenderingDeviceVulkan::texture_clear(RID p_texture, const Color &p_color, image_memory_barrier.subresourceRange.baseArrayLayer = src_tex->base_layer + p_base_layer; image_memory_barrier.subresourceRange.layerCount = p_layers; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, valid_texture_stages, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); } return OK; @@ -2991,6 +3049,19 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF Vector<VkAttachmentReference> depth_stencil_references; Vector<VkAttachmentReference> resolve_references; + // Set up a dependencies from/to external equivalent to the default (implicit) one, and then amend them + const VkPipelineStageFlags default_access_mask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; // From Section 7.1 of Vulkan API Spec v1.1.148 + + VkPipelineStageFlags reading_stages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; + VkSubpassDependency dependencies[2] = { { VK_SUBPASS_EXTERNAL, 0, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, 0, default_access_mask, 0 }, + { 0, VK_SUBPASS_EXTERNAL, VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, default_access_mask, 0, 0 } }; + VkSubpassDependency &dependency_from_external = dependencies[0]; + VkSubpassDependency &dependency_to_external = dependencies[1]; + for (int i = 0; i < p_format.size(); i++) { ERR_FAIL_INDEX_V(p_format[i].format, DATA_FORMAT_MAX, VK_NULL_HANDLE); ERR_FAIL_INDEX_V(p_format[i].samples, TEXTURE_SAMPLES_MAX, VK_NULL_HANDLE); @@ -3006,11 +3077,16 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF bool is_sampled = p_format[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT; bool is_storage = p_format[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT; + // For each UNDEFINED, assume the prior use was a *read*, as we'd be discarding the output of a write + // Also, each UNDEFINED will do an immediate layout transition (write), s.t. we must ensure execution syncronization vs. + // the read. If this is a performance issue, one could track the actual last accessor of each resource, adding only that + // stage switch (is_depth_stencil ? p_initial_depth_action : p_initial_color_action) { case INITIAL_ACTION_CLEAR: { description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there + dependency_from_external.srcStageMask |= reading_stages; } break; case INITIAL_ACTION_KEEP: { if (p_format[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { @@ -3021,10 +3097,12 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + dependency_from_external.srcStageMask |= reading_stages; } else { description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there + dependency_from_external.srcStageMask |= reading_stages; } } break; case INITIAL_ACTION_DROP: { @@ -3036,10 +3114,12 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + dependency_from_external.srcStageMask |= reading_stages; } else { description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there + dependency_from_external.srcStageMask |= reading_stages; } } break; case INITIAL_ACTION_CONTINUE: { @@ -3055,6 +3135,7 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there + dependency_from_external.srcStageMask |= reading_stages; } } break; default: { @@ -3068,14 +3149,17 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, false); } else if (p_format[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { description.storeOp = VK_ATTACHMENT_STORE_OP_STORE; description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE; description.finalLayout = is_sampled ? VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + update_external_dependency_for_store(dependency_to_external, is_sampled, is_storage, true); } else { description.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; //don't care what is there + // TODO: What does this mean about the next usage (and thus appropriate dependency masks } } break; case FINAL_ACTION_DISCARD: { @@ -3128,9 +3212,24 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF } else if (p_format[i].usage_flags & TEXTURE_USAGE_RESOLVE_ATTACHMENT_BIT) { reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; resolve_references.push_back(reference); + // if resolves are done, we need to ensure the copy is safe + dependency_to_external.dstStageMask |= VK_PIPELINE_STAGE_TRANSFER_BIT; + dependency_to_external.dstAccessMask |= VK_ACCESS_TRANSFER_READ_BIT; } else { ERR_FAIL_V_MSG(VK_NULL_HANDLE, "Texture index " + itos(i) + " is neither color, depth stencil or resolve so it can't be used as attachment."); } + + // NOTE: Big Mallet Approach -- any layout transition causes a full barrier + if (reference.layout != description.initialLayout) { + // NOTE: this should be smarter based on the textures knowledge of it's previous role + dependency_from_external.srcStageMask |= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + dependency_from_external.srcAccessMask |= VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; + } + if (reference.layout != description.finalLayout) { + // NOTE: this should be smarter based on the textures knowledge of it's subsequent role + dependency_from_external.dstStageMask |= VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + dependency_from_external.dstAccessMask |= VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT; + } } ERR_FAIL_COND_V_MSG(depth_stencil_references.size() > 1, VK_NULL_HANDLE, @@ -3159,8 +3258,8 @@ VkRenderPass RenderingDeviceVulkan::_render_pass_create(const Vector<AttachmentF render_pass_create_info.pAttachments = attachments.ptr(); render_pass_create_info.subpassCount = 1; render_pass_create_info.pSubpasses = &subpass; - render_pass_create_info.dependencyCount = 0; - render_pass_create_info.pDependencies = nullptr; + render_pass_create_info.dependencyCount = 2; + render_pass_create_info.pDependencies = dependencies; VkRenderPass render_pass; VkResult res = vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass); @@ -3185,7 +3284,7 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c } int color_references; - VkRenderPass render_pass = _render_pass_create(p_format, INITIAL_ACTION_CLEAR, FINAL_ACTION_DISCARD, INITIAL_ACTION_CLEAR, FINAL_ACTION_DISCARD, &color_references); //actions don't matter for this use case + VkRenderPass render_pass = _render_pass_create(p_format, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, &color_references); //actions don't matter for this use case if (render_pass == VK_NULL_HANDLE) { //was likely invalid return INVALID_ID; @@ -3389,6 +3488,10 @@ RID RenderingDeviceVulkan::vertex_buffer_create(uint32_t p_size_bytes, const Vec _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); + ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(), + "Creating buffers with data is forbidden during creation of a draw list"); + ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(), + "Creating buffers with data is forbidden during creation of a draw list"); Buffer buffer; _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); @@ -3512,6 +3615,10 @@ RID RenderingDeviceVulkan::vertex_array_create(uint32_t p_vertex_count, VertexFo RID RenderingDeviceVulkan::index_buffer_create(uint32_t p_index_count, IndexBufferFormat p_format, const Vector<uint8_t> &p_data, bool p_use_restart_indices) { _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(), + "Creating buffers with data is forbidden during creation of a draw list"); + ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(), + "Creating buffers with data is forbidden during creation of a draw list"); ERR_FAIL_COND_V(p_index_count == 0, RID()); @@ -4258,6 +4365,10 @@ RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const Ve _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); + ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(), + "Creating buffers with data is forbidden during creation of a draw list"); + ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(), + "Creating buffers with data is forbidden during creation of a draw list"); Buffer buffer; Error err = _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY); @@ -4273,6 +4384,10 @@ RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const Ve RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, uint32_t p_usage) { _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(), + "Creating buffers with data is forbidden during creation of a draw list"); + ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(), + "Creating buffers with data is forbidden during creation of a draw list"); ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID()); @@ -4296,6 +4411,10 @@ RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Ve RID RenderingDeviceVulkan::texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data) { _THREAD_SAFE_METHOD_ + ERR_FAIL_COND_V_MSG(draw_list != nullptr && p_data.size(), RID(), + "Creating buffers with data is forbidden during creation of a draw list"); + ERR_FAIL_COND_V_MSG(compute_list != nullptr && p_data.size(), RID(), + "Creating buffers with data is forbidden during creation of a draw list"); uint32_t element_size = get_format_vertex_size(p_format); ERR_FAIL_COND_V_MSG(element_size == 0, RID(), "Format requested is not supported for texture buffers"); @@ -4885,44 +5004,27 @@ Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint ERR_FAIL_COND_V_MSG(draw_list && p_sync_with_draw, ERR_INVALID_PARAMETER, "Updating buffers in 'sync to draw' mode is forbidden during creation of a draw list"); + ERR_FAIL_COND_V_MSG(compute_list && p_sync_with_draw, ERR_INVALID_PARAMETER, + "Updating buffers in 'sync to draw' mode is forbidden during creation of a compute list"); - VkPipelineStageFlags dst_stage_mask; - VkAccessFlags dst_access; + // Protect subsequent updates... + VkPipelineStageFlags dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; + VkAccessFlags dst_access = VK_ACCESS_TRANSFER_WRITE_BIT; - Buffer *buffer = nullptr; - if (vertex_buffer_owner.owns(p_buffer)) { - dst_stage_mask = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; - dst_access = VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; - buffer = vertex_buffer_owner.getornull(p_buffer); - } else if (index_buffer_owner.owns(p_buffer)) { - dst_stage_mask = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; - dst_access = VK_ACCESS_INDEX_READ_BIT; - buffer = index_buffer_owner.getornull(p_buffer); - } else if (uniform_buffer_owner.owns(p_buffer)) { - dst_stage_mask = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - dst_access = VK_ACCESS_UNIFORM_READ_BIT; - buffer = uniform_buffer_owner.getornull(p_buffer); - } else if (texture_buffer_owner.owns(p_buffer)) { - dst_stage_mask = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - dst_access = VK_ACCESS_SHADER_READ_BIT; - buffer = &texture_buffer_owner.getornull(p_buffer)->buffer; - } else if (storage_buffer_owner.owns(p_buffer)) { - dst_stage_mask = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; - dst_access = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - buffer = storage_buffer_owner.getornull(p_buffer); - } else { + Buffer *buffer = _get_buffer_from_owner(p_buffer, dst_stage_mask, dst_access); + if (!buffer) { ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Buffer argument is not a valid buffer of any type."); } ERR_FAIL_COND_V_MSG(p_offset + p_size > buffer->size, ERR_INVALID_PARAMETER, "Attempted to write buffer (" + itos((p_offset + p_size) - buffer->size) + " bytes) past the end."); + _buffer_memory_barrier(buffer->buffer, p_offset, p_size, dst_stage_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_access, VK_ACCESS_TRANSFER_WRITE_BIT, p_sync_with_draw); Error err = _buffer_update(buffer, p_offset, (uint8_t *)p_data, p_size, p_sync_with_draw); if (err) { return err; } - _buffer_memory_barrier(buffer->buffer, p_offset, p_size, VK_PIPELINE_STAGE_TRANSFER_BIT, dst_stage_mask, VK_ACCESS_TRANSFER_WRITE_BIT, dst_access, p_sync_with_draw); #ifdef FORCE_FULL_BARRIER _full_barrier(p_sync_with_draw); #else @@ -4934,20 +5036,20 @@ Error RenderingDeviceVulkan::buffer_update(RID p_buffer, uint32_t p_offset, uint Vector<uint8_t> RenderingDeviceVulkan::buffer_get_data(RID p_buffer) { _THREAD_SAFE_METHOD_ - Buffer *buffer = nullptr; - if (vertex_buffer_owner.owns(p_buffer)) { - buffer = vertex_buffer_owner.getornull(p_buffer); - } else if (index_buffer_owner.owns(p_buffer)) { - buffer = index_buffer_owner.getornull(p_buffer); - } else if (texture_buffer_owner.owns(p_buffer)) { - buffer = &texture_buffer_owner.getornull(p_buffer)->buffer; - } else if (storage_buffer_owner.owns(p_buffer)) { - buffer = storage_buffer_owner.getornull(p_buffer); - } else { + // It could be this buffer was just created + VkPipelineShaderStageCreateFlags src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT; + VkAccessFlags src_access_mask = VK_ACCESS_TRANSFER_WRITE_BIT; + // Get the vulkan buffer and the potential stage/access possible + Buffer *buffer = _get_buffer_from_owner(p_buffer, src_stage_mask, src_access_mask); + if (!buffer) { ERR_FAIL_V_MSG(Vector<uint8_t>(), "Buffer is either invalid or this type of buffer can't be retrieved. Only Index and Vertex buffers allow retrieving."); } + // Make sure no one is using the buffer -- the "false" gets us to the same command buffer as below. + _buffer_memory_barrier(buffer->buffer, 0, buffer->size, src_stage_mask, src_access_mask, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT, false); + VkCommandBuffer command_buffer = frames[frame].setup_command_buffer; + Buffer tmp_buffer; _buffer_allocate(&tmp_buffer, buffer->size, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_CPU_ONLY); VkBufferCopy region; @@ -5614,7 +5716,7 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff image_memory_barrier.subresourceRange.baseArrayLayer = texture->base_layer; image_memory_barrier.subresourceRange.layerCount = texture->layers; - vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); texture->layout = VK_IMAGE_LAYOUT_GENERAL; @@ -6352,7 +6454,7 @@ void RenderingDeviceVulkan::draw_list_end() { image_memory_barrier.subresourceRange.baseArrayLayer = texture->base_layer; image_memory_barrier.subresourceRange.layerCount = texture->layers; - vkCmdPipelineBarrier(frames[frame].draw_command_buffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(frames[frame].draw_command_buffer, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); texture->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } @@ -6494,7 +6596,7 @@ void RenderingDeviceVulkan::compute_list_bind_uniform_set(ComputeListID p_list, image_memory_barrier.subresourceRange.baseArrayLayer = textures_to_sampled[i]->base_layer; image_memory_barrier.subresourceRange.layerCount = textures_to_sampled[i]->layers; - vkCmdPipelineBarrier(cl->command_buffer, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + vkCmdPipelineBarrier(cl->command_buffer, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); textures_to_sampled[i]->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -6691,13 +6793,13 @@ void RenderingDeviceVulkan::compute_list_add_barrier(ComputeListID p_list) { void RenderingDeviceVulkan::compute_list_end() { ERR_FAIL_COND(!compute_list); - + const VkPipelineStageFlags dest_stage_mask = VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT; for (Set<Texture *>::Element *E = compute_list->state.textures_to_sampled_layout.front(); E; E = E->next()) { VkImageMemoryBarrier image_memory_barrier; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.pNext = nullptr; image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; - image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT; image_memory_barrier.oldLayout = E->get()->layout; image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; @@ -6710,7 +6812,8 @@ void RenderingDeviceVulkan::compute_list_end() { image_memory_barrier.subresourceRange.baseArrayLayer = E->get()->base_layer; image_memory_barrier.subresourceRange.layerCount = E->get()->layers; - vkCmdPipelineBarrier(compute_list->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); + // TODO: Look at the usages in the compute list and determine tighter dst stage and access masks based on some "final" usage equivalent + vkCmdPipelineBarrier(compute_list->command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, dest_stage_mask, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier); E->get()->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } @@ -6720,7 +6823,7 @@ void RenderingDeviceVulkan::compute_list_end() { #ifdef FORCE_FULL_BARRIER _full_barrier(true); #else - _memory_barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT, true); + _memory_barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, dest_stage_mask, VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_TRANSFER_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT, true); #endif } diff --git a/drivers/vulkan/rendering_device_vulkan.h b/drivers/vulkan/rendering_device_vulkan.h index 6f8bbc9c64..e6cbf2e01d 100644 --- a/drivers/vulkan/rendering_device_vulkan.h +++ b/drivers/vulkan/rendering_device_vulkan.h @@ -793,6 +793,7 @@ class RenderingDeviceVulkan : public RenderingDevice { 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); 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); + Buffer *_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &dst_stage_mask, VkAccessFlags &dst_access); /**********************/ /**** COMPUTE LIST ****/ diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index a6321ce058..e18e980fe2 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4225,6 +4225,7 @@ void EditorNode::_save_docks_to_config(Ref<ConfigFile> p_layout, const String &p p_layout->set_value(p_section, "dock_filesystem_split", filesystem_dock->get_split_offset()); p_layout->set_value(p_section, "dock_filesystem_display_mode", filesystem_dock->get_display_mode()); + p_layout->set_value(p_section, "dock_filesystem_file_sort", filesystem_dock->get_file_sort()); p_layout->set_value(p_section, "dock_filesystem_file_list_display_mode", filesystem_dock->get_file_list_display_mode()); for (int i = 0; i < vsplits.size(); i++) { @@ -4419,6 +4420,11 @@ void EditorNode::_load_docks_from_config(Ref<ConfigFile> p_layout, const String filesystem_dock->set_display_mode(dock_filesystem_display_mode); } + if (p_layout->has_section_key(p_section, "dock_filesystem_file_sort")) { + FileSystemDock::FileSortOption dock_filesystem_file_sort = FileSystemDock::FileSortOption(int(p_layout->get_value(p_section, "dock_filesystem_file_sort"))); + filesystem_dock->set_file_sort(dock_filesystem_file_sort); + } + if (p_layout->has_section_key(p_section, "dock_filesystem_file_list_display_mode")) { FileSystemDock::FileListDisplayMode dock_filesystem_file_list_display_mode = FileSystemDock::FileListDisplayMode(int(p_layout->get_value(p_section, "dock_filesystem_file_list_display_mode"))); filesystem_dock->set_file_list_display_mode(dock_filesystem_file_list_display_mode); diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 9e68ef2f35..48a9ca90c4 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -1172,7 +1172,7 @@ void EditorPropertyVector2::setup(double p_min, double p_max, double p_step, boo } EditorPropertyVector2::EditorPropertyVector2(bool p_force_wide) { - bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector2_editing"); + bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector2_editing")); BoxContainer *bc; @@ -1258,7 +1258,7 @@ void EditorPropertyRect2::setup(double p_min, double p_max, double p_step, bool } EditorPropertyRect2::EditorPropertyRect2(bool p_force_wide) { - bool horizontal = !p_force_wide && bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing")); + bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing")); BoxContainer *bc; @@ -1353,7 +1353,7 @@ void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, boo } EditorPropertyVector3::EditorPropertyVector3(bool p_force_wide) { - bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); + bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing")); BoxContainer *bc; @@ -1435,7 +1435,7 @@ void EditorPropertyVector2i::setup(int p_min, int p_max, bool p_no_slider) { } EditorPropertyVector2i::EditorPropertyVector2i(bool p_force_wide) { - bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector2_editing"); + bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector2_editing")); BoxContainer *bc; @@ -1521,7 +1521,7 @@ void EditorPropertyRect2i::setup(int p_min, int p_max, bool p_no_slider) { } EditorPropertyRect2i::EditorPropertyRect2i(bool p_force_wide) { - bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); + bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing")); BoxContainer *bc; @@ -1605,7 +1605,7 @@ void EditorPropertyVector3i::setup(int p_min, int p_max, bool p_no_slider) { } EditorPropertyVector3i::EditorPropertyVector3i(bool p_force_wide) { - bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); + bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing")); BoxContainer *bc; if (p_force_wide) { @@ -1690,7 +1690,7 @@ void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool } EditorPropertyPlane::EditorPropertyPlane(bool p_force_wide) { - bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing"); + bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing")); BoxContainer *bc; diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 0071f169ac..f675f60b7a 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -31,6 +31,7 @@ #include "filesystem_dock.h" #include "core/io/resource_loader.h" +#include "core/list.h" #include "core/os/dir_access.h" #include "core/os/file_access.h" #include "core/os/keyboard.h" @@ -46,13 +47,12 @@ #include "scene/resources/packed_scene.h" #include "servers/display_server.h" -Ref<Texture2D> FileSystemDock::_get_tree_item_icon(EditorFileSystemDirectory *p_dir, int p_idx) { +Ref<Texture2D> FileSystemDock::_get_tree_item_icon(bool p_is_valid, String p_file_type) { Ref<Texture2D> file_icon; - if (!p_dir->get_file_import_is_valid(p_idx)) { + if (!p_is_valid) { file_icon = get_theme_icon("ImportFail", "EditorIcons"); } else { - String file_type = p_dir->get_file_type(p_idx); - file_icon = (has_theme_icon(file_type, "EditorIcons")) ? get_theme_icon(file_type, "EditorIcons") : get_theme_icon("File", "EditorIcons"); + file_icon = (has_theme_icon(p_file_type, "EditorIcons")) ? get_theme_icon(p_file_type, "EditorIcons") : get_theme_icon("File", "EditorIcons"); } return file_icon; } @@ -94,15 +94,17 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory // Create all items for the files in the subdirectory. if (display_mode == DISPLAY_MODE_TREE_ONLY) { String main_scene = ProjectSettings::get_singleton()->get("application/run/main_scene"); + + // Build the list of the files to display. + List<FileInfo> file_list; for (int i = 0; i < p_dir->get_file_count(); i++) { String file_type = p_dir->get_file_type(i); - if (_is_file_type_disabled_by_feature_profile(file_type)) { // If type is disabled, file won't be displayed. continue; } - String file_name = p_dir->get_file(i); + String file_name = p_dir->get_file(i); if (searched_string.length() > 0) { if (file_name.to_lower().find(searched_string) < 0) { // The searched string is not in the file name, we skip it. @@ -113,10 +115,27 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory } } + FileInfo fi; + fi.name = p_dir->get_file(i); + fi.type = p_dir->get_file_type(i); + fi.import_broken = !p_dir->get_file_import_is_valid(i); + + file_list.push_back(fi); + } + + // Sort the file list if needed. + if (file_sort == FILE_SORT_TYPE) { + file_list.sort_custom<FileInfoExtensionComparator>(); + } + + // Build the tree. + for (List<FileInfo>::Element *E = file_list.front(); E; E = E->next()) { + FileInfo fi = E->get(); + TreeItem *file_item = tree->create_item(subdirectory_item); - file_item->set_text(0, file_name); - file_item->set_icon(0, _get_tree_item_icon(p_dir, i)); - String file_metadata = lpath.plus_file(file_name); + file_item->set_text(0, fi.name); + file_item->set_icon(0, _get_tree_item_icon(!fi.import_broken, fi.type)); + String file_metadata = lpath.plus_file(fi.name); file_item->set_metadata(0, file_metadata); if (!p_select_in_favorites && path == file_metadata) { file_item->select(0); @@ -220,7 +239,7 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo int index; EditorFileSystemDirectory *dir = EditorFileSystem::get_singleton()->find_file(fave, &index); if (dir) { - icon = _get_tree_item_icon(dir, index); + icon = _get_tree_item_icon(dir->get_file_import_is_valid(index), dir->get_file_type(index)); } else { icon = get_theme_icon("File", "EditorIcons"); } @@ -273,9 +292,9 @@ void FileSystemDock::_update_display_mode(bool p_force) { tree->show(); tree->set_v_size_flags(SIZE_EXPAND_FILL); if (display_mode == DISPLAY_MODE_TREE_ONLY) { - tree_search_box->show(); + toolbar2_hbc->show(); } else { - tree_search_box->hide(); + toolbar2_hbc->hide(); } _update_tree(_compute_uncollapsed_paths()); @@ -286,7 +305,7 @@ void FileSystemDock::_update_display_mode(bool p_force) { tree->show(); tree->set_v_size_flags(SIZE_EXPAND_FILL); tree->ensure_cursor_is_visible(); - tree_search_box->hide(); + toolbar2_hbc->hide(); _update_tree(_compute_uncollapsed_paths()); file_list_vb->show(); @@ -318,10 +337,14 @@ void FileSystemDock::_notification(int p_what) { files->connect("item_activated", callable_mp(this, &FileSystemDock::_file_list_activate_file)); button_hist_next->connect("pressed", callable_mp(this, &FileSystemDock::_fw_history)); button_hist_prev->connect("pressed", callable_mp(this, &FileSystemDock::_bw_history)); + tree_search_box->set_right_icon(get_theme_icon("Search", ei)); tree_search_box->set_clear_button_enabled(true); + tree_button_sort->set_icon(get_theme_icon("Sort", ei)); + file_list_search_box->set_right_icon(get_theme_icon("Search", ei)); file_list_search_box->set_clear_button_enabled(true); + file_list_button_sort->set_icon(get_theme_icon("Sort", ei)); button_hist_next->set_icon(get_theme_icon("Forward", ei)); button_hist_prev->set_icon(get_theme_icon("Back", ei)); @@ -387,8 +410,11 @@ void FileSystemDock::_notification(int p_what) { tree_search_box->set_right_icon(get_theme_icon("Search", ei)); tree_search_box->set_clear_button_enabled(true); + tree_button_sort->set_icon(get_theme_icon("Sort", ei)); + file_list_search_box->set_right_icon(get_theme_icon("Search", ei)); file_list_search_box->set_clear_button_enabled(true); + file_list_button_sort->set_icon(get_theme_icon("Sort", ei)); // Update always show folders. bool new_always_show_folders = bool(EditorSettings::get_singleton()->get("docks/filesystem/always_show_folders")); @@ -660,7 +686,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { const Color folder_color = get_theme_color("folder_icon_modulate", "FileDialog"); // Build the FileInfo list. - List<FileInfo> filelist; + List<FileInfo> file_list; if (path == "Favorites") { // Display the favorites. Vector<String> favorites = EditorSettings::get_singleton()->get_favorites(); @@ -698,7 +724,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { } if (searched_string.length() == 0 || fi.name.to_lower().find(searched_string) >= 0) { - filelist.push_back(fi); + file_list.push_back(fi); } } } @@ -719,7 +745,7 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { if (searched_string.length() > 0) { // Display the search results. - _search(EditorFileSystem::get_singleton()->get_filesystem(), &filelist, 128); + _search(EditorFileSystem::get_singleton()->get_filesystem(), &file_list, 128); } else { if (display_mode == DISPLAY_MODE_TREE_ONLY || always_show_folders) { // Display folders in the list. @@ -757,16 +783,21 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) { fi.type = efd->get_file_type(i); fi.import_broken = !efd->get_file_import_is_valid(i); - filelist.push_back(fi); + file_list.push_back(fi); } } - filelist.sort(); + file_list.sort(); + } + + // Sort the file list if needed. + if (file_sort == FILE_SORT_TYPE) { + file_list.sort_custom<FileInfoExtensionComparator>(); } // Fills the ItemList control node from the FileInfos. String main_scene = ProjectSettings::get_singleton()->get("application/run/main_scene"); String oi = "Object"; - for (List<FileInfo>::Element *E = filelist.front(); E; E = E->next()) { + for (List<FileInfo>::Element *E = file_list.front(); E; E = E->next()) { FileInfo *finfo = &(E->get()); String fname = finfo->name; String fpath = finfo->path; @@ -2516,6 +2547,35 @@ void FileSystemDock::_feature_profile_changed() { _update_display_mode(true); } +void FileSystemDock::set_file_sort(FileSortOption p_file_sort) { + for (int i = 0; i != FILE_SORT_MAX; i++) { + tree_button_sort->get_popup()->set_item_checked(i, (i == (int)p_file_sort)); + file_list_button_sort->get_popup()->set_item_checked(i, (i == (int)p_file_sort)); + } + file_sort = p_file_sort; + + // Update everything needed. + _update_tree(_compute_uncollapsed_paths()); + _update_file_list(true); +} + +void FileSystemDock::_file_sort_popup(int p_id) { + set_file_sort((FileSortOption)p_id); +} + +MenuButton *FileSystemDock::_create_file_menu_button() { + MenuButton *button = memnew(MenuButton); + button->set_flat(true); + button->set_tooltip(TTR("Sort files")); + + PopupMenu *p = button->get_popup(); + p->connect("id_pressed", callable_mp(this, &FileSystemDock::_file_sort_popup)); + p->add_radio_check_item("Sort by Name", FILE_SORT_NAME); + p->add_radio_check_item("Sort by Type", FILE_SORT_TYPE); + p->set_item_checked(file_sort, true); + return button; +} + void FileSystemDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_tree"), &FileSystemDock::_update_tree); @@ -2593,7 +2653,7 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { button_toggle_display_mode->set_tooltip(TTR("Toggle Split Mode")); toolbar_hbc->add_child(button_toggle_display_mode); - HBoxContainer *toolbar2_hbc = memnew(HBoxContainer); + toolbar2_hbc = memnew(HBoxContainer); toolbar2_hbc->add_theme_constant_override("separation", 0); top_vbc->add_child(toolbar2_hbc); @@ -2603,6 +2663,9 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { tree_search_box->connect("text_changed", callable_mp(this, &FileSystemDock::_search_changed), varray(tree_search_box)); toolbar2_hbc->add_child(tree_search_box); + tree_button_sort = _create_file_menu_button(); + toolbar2_hbc->add_child(tree_button_sort); + file_list_popup = memnew(PopupMenu); add_child(file_list_popup); @@ -2644,6 +2707,9 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) { file_list_search_box->connect("text_changed", callable_mp(this, &FileSystemDock::_search_changed), varray(file_list_search_box)); path_hb->add_child(file_list_search_box); + file_list_button_sort = _create_file_menu_button(); + path_hb->add_child(file_list_button_sort); + button_file_list_display_mode = memnew(Button); button_file_list_display_mode->set_flat(true); path_hb->add_child(button_file_list_display_mode); diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h index ec2a075834..f74e04ac2e 100644 --- a/editor/filesystem_dock.h +++ b/editor/filesystem_dock.h @@ -69,6 +69,12 @@ public: DISPLAY_MODE_SPLIT, }; + enum FileSortOption { + FILE_SORT_NAME = 0, + FILE_SORT_TYPE, + FILE_SORT_MAX, + }; + private: enum FileMenu { FILE_OPEN, @@ -95,6 +101,8 @@ private: FOLDER_COLLAPSE_ALL, }; + FileSortOption file_sort = FILE_SORT_NAME; + VBoxContainer *scanning_vb; ProgressBar *scanning_progress; VSplitContainer *split_box; @@ -109,8 +117,13 @@ private: Button *button_hist_next; Button *button_hist_prev; LineEdit *current_path; + + HBoxContainer *toolbar2_hbc; LineEdit *tree_search_box; + MenuButton *tree_button_sort; + LineEdit *file_list_search_box; + MenuButton *file_list_button_sort; String searched_string; Vector<String> uncollapsed_paths_before_search; @@ -173,7 +186,7 @@ private: ItemList *files; bool import_dock_needs_update; - Ref<Texture2D> _get_tree_item_icon(EditorFileSystemDirectory *p_dir, int p_idx); + Ref<Texture2D> _get_tree_item_icon(bool p_is_valid, String p_file_type); bool _create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path = false); Vector<String> _compute_uncollapsed_paths(); void _update_tree(const Vector<String> &p_uncollapsed_paths = Vector<String>(), bool p_uncollapse_root = false, bool p_select_in_favorites = false, bool p_unfold_path = false); @@ -238,6 +251,9 @@ private: void _search_changed(const String &p_text, const Control *p_from); + MenuButton *_create_file_menu_button(); + void _file_sort_popup(int p_id); + void _file_and_folders_fill_popup(PopupMenu *p_popup, Vector<String> p_paths, bool p_display_path_dependent_options = true); void _tree_rmb_select(const Vector2 &p_pos); void _tree_rmb_empty(const Vector2 &p_pos); @@ -256,6 +272,11 @@ private: return NaturalNoCaseComparator()(name, fi.name); } }; + struct FileInfoExtensionComparator { + bool operator()(const FileInfo &p_a, const FileInfo &p_b) const { + return NaturalNoCaseComparator()(p_a.name.get_extension() + p_a.name.get_basename(), p_b.name.get_extension() + p_b.name.get_basename()); + } + }; void _search(EditorFileSystemDirectory *p_path, List<FileInfo> *matches, int p_max_items); @@ -299,6 +320,9 @@ public: void set_display_mode(DisplayMode p_display_mode); DisplayMode get_display_mode() { return display_mode; } + void set_file_sort(FileSortOption p_file_sort); + FileSortOption get_file_sort() { return file_sort; } + void set_file_list_display_mode(FileListDisplayMode p_mode); FileListDisplayMode get_file_list_display_mode() { return file_list_display_mode; }; diff --git a/editor/plugins/audio_stream_editor_plugin.cpp b/editor/plugins/audio_stream_editor_plugin.cpp index b0f65af245..231f5588a4 100644 --- a/editor/plugins/audio_stream_editor_plugin.cpp +++ b/editor/plugins/audio_stream_editor_plugin.cpp @@ -44,8 +44,8 @@ void AudioStreamEditor::_notification(int p_what) { if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_ENTER_TREE) { _play_button->set_icon(get_theme_icon("MainPlay", "EditorIcons")); _stop_button->set_icon(get_theme_icon("Stop", "EditorIcons")); - _preview->set_frame_color(get_theme_color("dark_color_2", "Editor")); - set_frame_color(get_theme_color("dark_color_1", "Editor")); + _preview->set_color(get_theme_color("dark_color_2", "Editor")); + set_color(get_theme_color("dark_color_1", "Editor")); _indicator->update(); _preview->update(); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index aecbf817c4..a4eab6ab4a 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -2464,12 +2464,14 @@ void Node3DEditorViewport::_notification(int p_what) { subviewport_container->set_stretch_shrink(shrink ? 2 : 1); } - //update msaa if changed + // Update MSAA, screen-space AA and debanding if changed - int msaa_mode = ProjectSettings::get_singleton()->get("rendering/quality/screen_filters/msaa"); + const int msaa_mode = ProjectSettings::get_singleton()->get("rendering/quality/screen_filters/msaa"); viewport->set_msaa(Viewport::MSAA(msaa_mode)); - int ssaa_mode = GLOBAL_GET("rendering/quality/screen_filters/screen_space_aa"); + const int ssaa_mode = GLOBAL_GET("rendering/quality/screen_filters/screen_space_aa"); viewport->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode)); + const bool use_debanding = GLOBAL_GET("rendering/quality/screen_filters/use_debanding"); + viewport->set_use_debanding(use_debanding); bool show_info = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION)); if (show_info != info_label->is_visible()) { diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index cb56358ae6..7f1b5347cf 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -1697,13 +1697,18 @@ void CustomPropertyEditor::config_value_editors(int p_amount, int p_columns, int int cell_width = 95; int cell_height = 25; int cell_margin = 5; - int hor_spacing = 5; // Spacing between labels and their values - int rows = ((p_amount - 1) / p_columns) + 1; set_size(Size2(cell_margin + p_label_w + (cell_width + cell_margin + p_label_w) * p_columns, cell_margin + (cell_height + cell_margin) * rows) * EDSCALE); for (int i = 0; i < MAX_VALUE_EDITORS; i++) { + value_label[i]->get_parent()->remove_child(value_label[i]); + value_editor[i]->get_parent()->remove_child(value_editor[i]); + + int box_id = i / p_columns; + value_hboxes[box_id]->add_child(value_label[i]); + value_hboxes[box_id]->add_child(value_editor[i]); + if (i < MAX_VALUE_EDITORS / 4) { if (i <= p_amount / 4) { value_hboxes[i]->show(); @@ -1712,16 +1717,10 @@ void CustomPropertyEditor::config_value_editors(int p_amount, int p_columns, int } } - int c = i % p_columns; - int r = i / p_columns; - if (i < p_amount) { value_editor[i]->show(); value_label[i]->show(); value_label[i]->set_text(i < p_strings.size() ? p_strings[i] : String("")); - value_editor[i]->set_position(Point2(cell_margin + p_label_w + hor_spacing + (cell_width + cell_margin + p_label_w + hor_spacing) * c, cell_margin + (cell_height + cell_margin) * r) * EDSCALE); - value_editor[i]->set_size(Size2(cell_width, cell_height)); - value_label[i]->set_position(Point2(cell_margin + (cell_width + cell_margin + p_label_w + hor_spacing) * c, cell_margin + (cell_height + cell_margin) * r) * EDSCALE); value_editor[i]->set_editable(!read_only); } else { value_editor[i]->hide(); diff --git a/editor/shader_globals_editor.cpp b/editor/shader_globals_editor.cpp index 04fbac3463..915aec6d9a 100644 --- a/editor/shader_globals_editor.cpp +++ b/editor/shader_globals_editor.cpp @@ -284,7 +284,13 @@ static Variant create_var(RS::GlobalVariableType p_type) { return Vector3i(); } case RS::GLOBAL_VAR_TYPE_UVEC4: { - return Rect2i(); + Vector<int> v4; + v4.resize(4); + v4.write[0] = 0; + v4.write[1] = 0; + v4.write[2] = 0; + v4.write[3] = 0; + return v4; } case RS::GLOBAL_VAR_TYPE_FLOAT: { return 0.0; @@ -324,7 +330,7 @@ static Variant create_var(RS::GlobalVariableType p_type) { } case RS::GLOBAL_VAR_TYPE_MAT4: { Vector<real_t> xform; - xform.resize(4); + xform.resize(16); xform.write[0] = 1; xform.write[1] = 0; xform.write[2] = 0; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 943a49060f..5250115608 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -577,6 +577,12 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas GDScriptParser::DataType datatype = member.constant->get_datatype(); if (member.constant->initializer) { + if (member.constant->initializer->type == GDScriptParser::Node::ARRAY) { + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(member.constant->initializer)); + } else if (member.constant->initializer->type == GDScriptParser::Node::DICTIONARY) { + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(member.constant->initializer)); + } + if (!member.constant->initializer->is_constant) { push_error(R"(Initializer for a constant must be a constant expression.)", member.constant->initializer); } @@ -1113,6 +1119,11 @@ void GDScriptAnalyzer::resolve_constant(GDScriptParser::ConstantNode *p_constant GDScriptParser::DataType type; reduce_expression(p_constant->initializer); + if (p_constant->initializer->type == GDScriptParser::Node::ARRAY) { + const_fold_array(static_cast<GDScriptParser::ArrayNode *>(p_constant->initializer)); + } else if (p_constant->initializer->type == GDScriptParser::Node::DICTIONARY) { + const_fold_dictionary(static_cast<GDScriptParser::DictionaryNode *>(p_constant->initializer)); + } if (!p_constant->initializer->is_constant) { push_error(vformat(R"(Assigned value for constant "%s" isn't a constant expression.)", p_constant->identifier->name), p_constant->initializer); @@ -1422,22 +1433,9 @@ void GDScriptAnalyzer::reduce_expression(GDScriptParser::ExpressionNode *p_expre } void GDScriptAnalyzer::reduce_array(GDScriptParser::ArrayNode *p_array) { - bool all_is_constant = true; - for (int i = 0; i < p_array->elements.size(); i++) { GDScriptParser::ExpressionNode *element = p_array->elements[i]; reduce_expression(element); - all_is_constant = all_is_constant && element->is_constant; - } - - if (all_is_constant) { - Array array; - array.resize(p_array->elements.size()); - for (int i = 0; i < p_array->elements.size(); i++) { - array[i] = p_array->elements[i]->reduced_value; - } - p_array->is_constant = true; - p_array->reduced_value = array; } // It's array in any case. @@ -1984,8 +1982,6 @@ void GDScriptAnalyzer::reduce_cast(GDScriptParser::CastNode *p_cast) { } void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dictionary) { - bool all_is_constant = true; - HashMap<Variant, GDScriptParser::ExpressionNode *, VariantHasher, VariantComparator> elements; for (int i = 0; i < p_dictionary->elements.size(); i++) { @@ -1994,7 +1990,6 @@ void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dicti reduce_expression(element.key); } reduce_expression(element.value); - all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant; if (element.key->is_constant) { if (elements.has(element.key->reduced_value)) { @@ -2005,16 +2000,6 @@ void GDScriptAnalyzer::reduce_dictionary(GDScriptParser::DictionaryNode *p_dicti } } - if (all_is_constant) { - Dictionary dict; - for (int i = 0; i < p_dictionary->elements.size(); i++) { - const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; - dict[element.key->reduced_value] = element.value->reduced_value; - } - p_dictionary->is_constant = true; - p_dictionary->reduced_value = dict; - } - // It's dictionary in any case. GDScriptParser::DataType dict_type; dict_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; @@ -2737,6 +2722,46 @@ void GDScriptAnalyzer::reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op) p_unary_op->set_datatype(result); } +void GDScriptAnalyzer::const_fold_array(GDScriptParser::ArrayNode *p_array) { + bool all_is_constant = true; + + for (int i = 0; i < p_array->elements.size(); i++) { + GDScriptParser::ExpressionNode *element = p_array->elements[i]; + all_is_constant = all_is_constant && element->is_constant; + if (!all_is_constant) { + return; + } + } + + Array array; + array.resize(p_array->elements.size()); + for (int i = 0; i < p_array->elements.size(); i++) { + array[i] = p_array->elements[i]->reduced_value; + } + p_array->is_constant = true; + p_array->reduced_value = array; +} + +void GDScriptAnalyzer::const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary) { + bool all_is_constant = true; + + for (int i = 0; i < p_dictionary->elements.size(); i++) { + const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; + all_is_constant = all_is_constant && element.key->is_constant && element.value->is_constant; + if (!all_is_constant) { + return; + } + } + + Dictionary dict; + for (int i = 0; i < p_dictionary->elements.size(); i++) { + const GDScriptParser::DictionaryNode::Pair &element = p_dictionary->elements[i]; + dict[element.key->reduced_value] = element.value->reduced_value; + } + p_dictionary->is_constant = true; + p_dictionary->reduced_value = dict; +} + GDScriptParser::DataType GDScriptAnalyzer::type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source) { GDScriptParser::DataType result; result.is_constant = true; diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index c3911cce76..f3cbb320b7 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -89,6 +89,9 @@ class GDScriptAnalyzer { void reduce_ternary_op(GDScriptParser::TernaryOpNode *p_ternary_op); void reduce_unary_op(GDScriptParser::UnaryOpNode *p_unary_op); + void const_fold_array(GDScriptParser::ArrayNode *p_array); + void const_fold_dictionary(GDScriptParser::DictionaryNode *p_dictionary); + // Helpers. GDScriptParser::DataType type_from_variant(const Variant &p_value, const GDScriptParser::Node *p_source); GDScriptParser::DataType type_from_metatype(const GDScriptParser::DataType &p_meta_type) const; diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp index eabf53581d..cc9e87b882 100644 --- a/modules/gdscript/gdscript_byte_codegen.cpp +++ b/modules/gdscript/gdscript_byte_codegen.cpp @@ -580,8 +580,8 @@ void GDScriptByteCodeGenerator::write_endif() { } void GDScriptByteCodeGenerator::write_for(const Address &p_variable, const Address &p_list) { - int counter_pos = increase_stack() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); - int container_pos = increase_stack() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); + int counter_pos = add_temporary() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); + int container_pos = add_temporary() | (GDScriptFunction::ADDR_TYPE_STACK << GDScriptFunction::ADDR_BITS); current_breaks_to_patch.push_back(List<int>()); @@ -629,7 +629,9 @@ void GDScriptByteCodeGenerator::write_endfor() { } current_breaks_to_patch.pop_back(); - current_stack_size -= 2; // Remove loop temporaries. + // Remove loop temporaries. + pop_temporary(); + pop_temporary(); } void GDScriptByteCodeGenerator::start_while_condition() { diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index db02079d26..fe34d6cba3 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -51,12 +51,11 @@ class GDScriptCompiler { GDScriptCodeGenerator *generator = nullptr; Map<StringName, GDScriptCodeGenerator::Address> parameters; Map<StringName, GDScriptCodeGenerator::Address> locals; - List<Set<StringName>> locals_in_scope; + List<Map<StringName, GDScriptCodeGenerator::Address>> locals_stack; GDScriptCodeGenerator::Address add_local(const StringName &p_name, const GDScriptDataType &p_type) { uint32_t addr = generator->add_local(p_name, p_type); locals[p_name] = GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::LOCAL_VARIABLE, addr, p_type); - locals_in_scope.back()->get().insert(p_name); return locals[p_name]; } @@ -102,17 +101,14 @@ class GDScriptCompiler { } void start_block() { - Set<StringName> scope; - locals_in_scope.push_back(scope); + Map<StringName, GDScriptCodeGenerator::Address> old_locals = locals; + locals_stack.push_back(old_locals); generator->start_block(); } void end_block() { - Set<StringName> &scope = locals_in_scope.back()->get(); - for (Set<StringName>::Element *E = scope.front(); E; E = E->next()) { - locals.erase(E->get()); - } - locals_in_scope.pop_back(); + locals = locals_stack.back()->get(); + locals_stack.pop_back(); generator->end_block(); } }; diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp index 68d9984b43..931b683a44 100644 --- a/modules/gdscript/tests/test_gdscript.cpp +++ b/modules/gdscript/tests/test_gdscript.cpp @@ -30,10 +30,13 @@ #include "test_gdscript.h" +#include "core/io/file_access_pack.h" #include "core/os/file_access.h" #include "core/os/main_loop.h" #include "core/os/os.h" +#include "core/project_settings.h" #include "core/string_builder.h" +#include "scene/resources/packed_scene.h" #include "modules/gdscript/gdscript_analyzer.h" #include "modules/gdscript/gdscript_compiler.h" @@ -179,6 +182,60 @@ static void test_compiler(const String &p_code, const String &p_script_path, con } } +void init_autoloads() { + Map<StringName, ProjectSettings::AutoloadInfo> autoloads = ProjectSettings::get_singleton()->get_autoload_list(); + + // First pass, add the constants so they exist before any script is loaded. + for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { + const ProjectSettings::AutoloadInfo &info = E->get(); + + if (info.is_singleton) { + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptServer::get_language(i)->add_global_constant(info.name, Variant()); + } + } + } + + // Second pass, load into global constants. + for (Map<StringName, ProjectSettings::AutoloadInfo>::Element *E = autoloads.front(); E; E = E->next()) { + const ProjectSettings::AutoloadInfo &info = E->get(); + + if (!info.is_singleton) { + // Skip non-singletons since we don't have a scene tree here anyway. + continue; + } + + RES res = ResourceLoader::load(info.path); + ERR_CONTINUE_MSG(res.is_null(), "Can't autoload: " + info.path); + Node *n = nullptr; + if (res->is_class("PackedScene")) { + Ref<PackedScene> ps = res; + n = ps->instance(); + } else if (res->is_class("Script")) { + Ref<Script> script_res = res; + StringName ibt = script_res->get_instance_base_type(); + bool valid_type = ClassDB::is_parent_class(ibt, "Node"); + ERR_CONTINUE_MSG(!valid_type, "Script does not inherit a Node: " + info.path); + + Object *obj = ClassDB::instance(ibt); + + ERR_CONTINUE_MSG(obj == nullptr, + "Cannot instance script for autoload, expected 'Node' inheritance, got: " + + String(ibt)); + + n = Object::cast_to<Node>(obj); + n->set_script(script_res); + } + + ERR_CONTINUE_MSG(!n, "Path in autoload not a node or script: " + info.path); + n->set_name(info.name); + + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptServer::get_language(i)->add_global_constant(info.name, n); + } + } +} + void test(TestType p_type) { List<String> cmdlargs = OS::get_singleton()->get_cmdline_args(); @@ -195,6 +252,21 @@ void test(TestType p_type) { FileAccessRef fa = FileAccess::open(test, FileAccess::READ); ERR_FAIL_COND_MSG(!fa, "Could not open file: " + test); + // Init PackedData since it's used by ProjectSettings. + PackedData *packed_data = memnew(PackedData); + + // Setup project settings since it's needed by the languages to get the global scripts. + // This also sets up the base resource path. + Error err = ProjectSettings::get_singleton()->setup(fa->get_path_absolute().get_base_dir(), String(), true); + if (err) { + print_line("Could not load project settings."); + // Keep going since some scripts still work without this. + } + + // Initialize the language for the test routine. + ScriptServer::init_languages(); + init_autoloads(); + Vector<uint8_t> buf; int flen = fa->get_len(); buf.resize(fa->get_len() + 1); @@ -226,6 +298,10 @@ void test(TestType p_type) { case TEST_BYTECODE: print_line("Not implemented."); } + + // Destroy stuff we set up earlier. + ScriptServer::finish_languages(); + memdelete(packed_data); } } // namespace TestGDScript diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index d962d91287..01c674d086 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -4783,8 +4783,8 @@ VisualScriptEditor::VisualScriptEditor() { graph->set_v_size_flags(Control::SIZE_EXPAND_FILL); graph->set_anchors_and_margins_preset(Control::PRESET_WIDE); graph->connect("node_selected", callable_mp(this, &VisualScriptEditor::_node_selected)); - graph->connect("_begin_node_move", callable_mp(this, &VisualScriptEditor::_begin_node_move)); - graph->connect("_end_node_move", callable_mp(this, &VisualScriptEditor::_end_node_move)); + graph->connect("begin_node_move", callable_mp(this, &VisualScriptEditor::_begin_node_move)); + graph->connect("end_node_move", callable_mp(this, &VisualScriptEditor::_end_node_move)); graph->connect("delete_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_delete)); graph->connect("duplicate_nodes_request", callable_mp(this, &VisualScriptEditor::_on_nodes_duplicate)); graph->connect("gui_input", callable_mp(this, &VisualScriptEditor::_graph_gui_input)); diff --git a/platform/iphone/in_app_store.h b/platform/iphone/in_app_store.h index ccf31876a9..1b8f84ec7b 100644 --- a/platform/iphone/in_app_store.h +++ b/platform/iphone/in_app_store.h @@ -35,6 +35,17 @@ #include "core/class_db.h" +#ifdef __OBJC__ +@class GodotProductsDelegate; +@class GodotTransactionsObserver; + +typedef GodotProductsDelegate InAppStoreProductDelegate; +typedef GodotTransactionsObserver InAppStoreTransactionObserver; +#else +typedef void InAppStoreProductDelegate; +typedef void InAppStoreTransactionObserver; +#endif + class InAppStore : public Object { GDCLASS(InAppStore, Object); @@ -43,6 +54,9 @@ class InAppStore : public Object { List<Variant> pending_events; + InAppStoreProductDelegate *products_request_delegate; + InAppStoreTransactionObserver *transactions_observer; + public: Error request_product_info(Dictionary p_params); Error restore_purchases(); diff --git a/platform/iphone/in_app_store.mm b/platform/iphone/in_app_store.mm index 2b973dfbcf..3eec9dae69 100644 --- a/platform/iphone/in_app_store.mm +++ b/platform/iphone/in_app_store.mm @@ -32,24 +32,22 @@ #include "in_app_store.h" -extern "C" { #import <Foundation/Foundation.h> #import <StoreKit/StoreKit.h> -}; -bool auto_finish_transactions = true; -NSMutableDictionary *pending_transactions = [NSMutableDictionary dictionary]; -static NSArray *latestProducts; +InAppStore *InAppStore::instance = NULL; @interface SKProduct (LocalizedPrice) @property(nonatomic, readonly) NSString *localizedPrice; + @end //----------------------------------// // SKProduct extension //----------------------------------// @implementation SKProduct (LocalizedPrice) + - (NSString *)localizedPrice { NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init]; [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4]; @@ -61,29 +59,91 @@ static NSArray *latestProducts; @end -InAppStore *InAppStore::instance = NULL; - -void InAppStore::_bind_methods() { - ClassDB::bind_method(D_METHOD("request_product_info"), &InAppStore::request_product_info); - ClassDB::bind_method(D_METHOD("restore_purchases"), &InAppStore::restore_purchases); - ClassDB::bind_method(D_METHOD("purchase"), &InAppStore::purchase); +@interface GodotProductsDelegate : NSObject <SKProductsRequestDelegate> - ClassDB::bind_method(D_METHOD("get_pending_event_count"), &InAppStore::get_pending_event_count); - ClassDB::bind_method(D_METHOD("pop_pending_event"), &InAppStore::pop_pending_event); - ClassDB::bind_method(D_METHOD("finish_transaction"), &InAppStore::finish_transaction); - ClassDB::bind_method(D_METHOD("set_auto_finish_transaction"), &InAppStore::set_auto_finish_transaction); -}; +@property(nonatomic, strong) NSMutableArray *loadedProducts; +@property(nonatomic, strong) NSMutableArray *pendingRequests; -@interface ProductsDelegate : NSObject <SKProductsRequestDelegate> { -}; +- (void)performRequestWithProductIDs:(NSSet *)productIDs; +- (Error)purchaseProductWithProductID:(NSString *)productID; +- (void)reset; @end -@implementation ProductsDelegate +@implementation GodotProductsDelegate + +- (instancetype)init { + self = [super init]; + + if (self) { + [self godot_commonInit]; + } + + return self; +} + +- (void)godot_commonInit { + self.loadedProducts = [NSMutableArray new]; + self.pendingRequests = [NSMutableArray new]; +} + +- (void)performRequestWithProductIDs:(NSSet *)productIDs { + SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:productIDs]; + + request.delegate = self; + [self.pendingRequests addObject:request]; + [request start]; +} + +- (Error)purchaseProductWithProductID:(NSString *)productID { + SKProduct *product = nil; + + NSLog(@"searching for product!"); + + if (self.loadedProducts) { + for (SKProduct *storedProduct in self.loadedProducts) { + if ([storedProduct.productIdentifier isEqualToString:productID]) { + product = storedProduct; + break; + } + } + } + + if (!product) { + return ERR_INVALID_PARAMETER; + } + + NSLog(@"product found!"); + + SKPayment *payment = [SKPayment paymentWithProduct:product]; + [[SKPaymentQueue defaultQueue] addPayment:payment]; + + NSLog(@"purchase sent!"); + + return OK; +} + +- (void)reset { + [self.loadedProducts removeAllObjects]; + [self.pendingRequests removeAllObjects]; +} + +- (void)request:(SKRequest *)request didFailWithError:(NSError *)error { + [self.pendingRequests removeObject:request]; + + Dictionary ret; + ret["type"] = "product_info"; + ret["result"] = "error"; + ret["error"] = String::utf8([error.localizedDescription UTF8String]); + + InAppStore::get_singleton()->_post_event(ret); +} - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { + [self.pendingRequests removeObject:request]; + NSArray *products = response.products; - latestProducts = products; + [self.loadedProducts addObjectsFromArray:products]; Dictionary ret; ret["type"] = "product_info"; @@ -107,7 +167,8 @@ void InAppStore::_bind_methods() { ids.push_back(String::utf8([product.productIdentifier UTF8String])); localized_prices.push_back(String::utf8([product.localizedPrice UTF8String])); currency_codes.push_back(String::utf8([[[product priceLocale] objectForKey:NSLocaleCurrencyCode] UTF8String])); - }; + } + ret["titles"] = titles; ret["descriptions"] = descriptions; ret["prices"] = prices; @@ -119,50 +180,54 @@ void InAppStore::_bind_methods() { for (NSString *ipid in response.invalidProductIdentifiers) { invalid_ids.push_back(String::utf8([ipid UTF8String])); - }; + } + ret["invalid_ids"] = invalid_ids; InAppStore::get_singleton()->_post_event(ret); -}; +} @end -Error InAppStore::request_product_info(Dictionary p_params) { - ERR_FAIL_COND_V(!p_params.has("product_ids"), ERR_INVALID_PARAMETER); +@interface GodotTransactionsObserver : NSObject <SKPaymentTransactionObserver> - PackedStringArray pids = p_params["product_ids"]; - printf("************ request product info! %i\n", pids.size()); +@property(nonatomic, assign) BOOL shouldAutoFinishTransactions; +@property(nonatomic, strong) NSMutableDictionary *pendingTransactions; - NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:pids.size()]; - for (int i = 0; i < pids.size(); i++) { - printf("******** adding %s to product list\n", pids[i].utf8().get_data()); - NSString *pid = [[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()]; - [array addObject:pid]; - }; +- (void)finishTransactionWithProductID:(NSString *)productID; +- (void)reset; - NSSet *products = [[NSSet alloc] initWithArray:array]; - SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:products]; +@end - ProductsDelegate *delegate = [[ProductsDelegate alloc] init]; +@implementation GodotTransactionsObserver - request.delegate = delegate; - [request start]; +- (instancetype)init { + self = [super init]; - return OK; -}; + if (self) { + [self godot_commonInit]; + } -Error InAppStore::restore_purchases() { - printf("restoring purchases!\n"); - [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; + return self; +} - return OK; -}; +- (void)godot_commonInit { + self.pendingTransactions = [NSMutableDictionary new]; +} -@interface TransObserver : NSObject <SKPaymentTransactionObserver> { -}; -@end +- (void)finishTransactionWithProductID:(NSString *)productID { + SKPaymentTransaction *transaction = self.pendingTransactions[productID]; + + if (transaction) { + [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; + } -@implementation TransObserver + self.pendingTransactions[productID] = nil; +} + +- (void)reset { + [self.pendingTransactions removeAllObjects]; +} - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { printf("transactions updated!\n"); @@ -190,6 +255,7 @@ Error InAppStore::restore_purchases() { receipt = [NSData dataWithContentsOfURL:receiptFileURL]; NSString *receipt_to_send = nil; + if (receipt != nil) { receipt_to_send = [receipt base64EncodedStringWithOptions:0]; } @@ -200,13 +266,13 @@ Error InAppStore::restore_purchases() { InAppStore::get_singleton()->_post_event(ret); - if (auto_finish_transactions) { + if (self.shouldAutoFinishTransactions) { [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; } else { - [pending_transactions setObject:transaction forKey:transaction.payment.productIdentifier]; + self.pendingTransactions[transaction.payment.productIdentifier] = transaction; } - }; break; + } break; case SKPaymentTransactionStateFailed: { printf("status transaction failed!\n"); String pid = String::utf8([transaction.payment.productIdentifier UTF8String]); @@ -231,95 +297,119 @@ Error InAppStore::restore_purchases() { } break; default: { printf("status default %i!\n", (int)transaction.transactionState); - }; break; - }; - }; -}; + } break; + } + } +} @end -Error InAppStore::purchase(Dictionary p_params) { - ERR_FAIL_COND_V(![SKPaymentQueue canMakePayments], ERR_UNAVAILABLE); - if (![SKPaymentQueue canMakePayments]) - return ERR_UNAVAILABLE; +void InAppStore::_bind_methods() { + ClassDB::bind_method(D_METHOD("request_product_info"), &InAppStore::request_product_info); + ClassDB::bind_method(D_METHOD("restore_purchases"), &InAppStore::restore_purchases); + ClassDB::bind_method(D_METHOD("purchase"), &InAppStore::purchase); - printf("purchasing!\n"); - ERR_FAIL_COND_V(!p_params.has("product_id"), ERR_INVALID_PARAMETER); + ClassDB::bind_method(D_METHOD("get_pending_event_count"), &InAppStore::get_pending_event_count); + ClassDB::bind_method(D_METHOD("pop_pending_event"), &InAppStore::pop_pending_event); + ClassDB::bind_method(D_METHOD("finish_transaction"), &InAppStore::finish_transaction); + ClassDB::bind_method(D_METHOD("set_auto_finish_transaction"), &InAppStore::set_auto_finish_transaction); +} - NSString *pid = [[NSString alloc] initWithUTF8String:String(p_params["product_id"]).utf8().get_data()]; +Error InAppStore::request_product_info(Dictionary p_params) { + ERR_FAIL_COND_V(!p_params.has("product_ids"), ERR_INVALID_PARAMETER); - SKProduct *product = nil; + PackedStringArray pids = p_params["product_ids"]; + printf("************ request product info! %i\n", pids.size()); - if (latestProducts) { - for (SKProduct *storedProduct in latestProducts) { - if ([storedProduct.productIdentifier isEqualToString:pid]) { - product = storedProduct; - break; - } - } - } + NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:pids.size()]; + for (int i = 0; i < pids.size(); i++) { + printf("******** adding %s to product list\n", pids[i].utf8().get_data()); + NSString *pid = [[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()]; + [array addObject:pid]; + }; - if (!product) { - return ERR_INVALID_PARAMETER; - } + NSSet *products = [[NSSet alloc] initWithArray:array]; - SKPayment *payment = [SKPayment paymentWithProduct:product]; - SKPaymentQueue *defq = [SKPaymentQueue defaultQueue]; - [defq addPayment:payment]; - printf("purchase sent!\n"); + [products_request_delegate performRequestWithProductIDs:products]; return OK; -}; +} + +Error InAppStore::restore_purchases() { + printf("restoring purchases!\n"); + [[SKPaymentQueue defaultQueue] restoreCompletedTransactions]; + + return OK; +} + +Error InAppStore::purchase(Dictionary p_params) { + ERR_FAIL_COND_V(![SKPaymentQueue canMakePayments], ERR_UNAVAILABLE); + if (![SKPaymentQueue canMakePayments]) { + return ERR_UNAVAILABLE; + } + + printf("purchasing!\n"); + Dictionary params = p_params; + ERR_FAIL_COND_V(!params.has("product_id"), ERR_INVALID_PARAMETER); + + NSString *pid = [[NSString alloc] initWithUTF8String:String(params["product_id"]).utf8().get_data()]; + + return [products_request_delegate purchaseProductWithProductID:pid]; +} int InAppStore::get_pending_event_count() { return pending_events.size(); -}; +} Variant InAppStore::pop_pending_event() { Variant front = pending_events.front()->get(); pending_events.pop_front(); return front; -}; +} void InAppStore::_post_event(Variant p_event) { pending_events.push_back(p_event); -}; +} void InAppStore::_record_purchase(String product_id) { String skey = "purchased/" + product_id; NSString *key = [[NSString alloc] initWithUTF8String:skey.utf8().get_data()]; [[NSUserDefaults standardUserDefaults] setBool:YES forKey:key]; [[NSUserDefaults standardUserDefaults] synchronize]; -}; +} InAppStore *InAppStore::get_singleton() { return instance; -}; +} InAppStore::InAppStore() { ERR_FAIL_COND(instance != NULL); instance = this; - auto_finish_transactions = false; - TransObserver *observer = [[TransObserver alloc] init]; - [[SKPaymentQueue defaultQueue] addTransactionObserver:observer]; - //pending_transactions = [NSMutableDictionary dictionary]; -}; + products_request_delegate = [[GodotProductsDelegate alloc] init]; + transactions_observer = [[GodotTransactionsObserver alloc] init]; + + [[SKPaymentQueue defaultQueue] addTransactionObserver:transactions_observer]; +} void InAppStore::finish_transaction(String product_id) { NSString *prod_id = [NSString stringWithCString:product_id.utf8().get_data() encoding:NSUTF8StringEncoding]; - if ([pending_transactions objectForKey:prod_id]) { - [[SKPaymentQueue defaultQueue] finishTransaction:[pending_transactions objectForKey:prod_id]]; - [pending_transactions removeObjectForKey:prod_id]; - } -}; + [transactions_observer finishTransactionWithProductID:prod_id]; +} void InAppStore::set_auto_finish_transaction(bool b) { - auto_finish_transactions = b; + transactions_observer.shouldAutoFinishTransactions = b; } -InAppStore::~InAppStore() {} +InAppStore::~InAppStore() { + [products_request_delegate reset]; + [transactions_observer reset]; + + products_request_delegate = nil; + [[SKPaymentQueue defaultQueue] removeTransactionObserver:transactions_observer]; + transactions_observer = nil; +} #endif diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm index cd5b71890c..1ad7117b39 100644 --- a/platform/osx/display_server_osx.mm +++ b/platform/osx/display_server_osx.mm @@ -63,6 +63,8 @@ #define DS_OSX ((DisplayServerOSX *)(DisplayServerOSX::get_singleton())) +static bool ignore_momentum_scroll = false; + static void _get_key_modifier_state(unsigned int p_osx_state, Ref<InputEventWithModifiers> r_state) { r_state->set_shift((p_osx_state & NSEventModifierFlagShift)); r_state->set_control((p_osx_state & NSEventModifierFlagControl)); @@ -1304,6 +1306,8 @@ static int remapKey(unsigned int key, unsigned int state) { ERR_FAIL_COND(!DS_OSX->windows.has(window_id)); DisplayServerOSX::WindowData &wd = DS_OSX->windows[window_id]; + ignore_momentum_scroll = true; + // Ignore all input if IME input is in progress if (!imeInputEventInProgress) { NSString *characters = [event characters]; @@ -1348,6 +1352,8 @@ static int remapKey(unsigned int key, unsigned int state) { } - (void)flagsChanged:(NSEvent *)event { + ignore_momentum_scroll = true; + // Ignore all input if IME input is in progress if (!imeInputEventInProgress) { DisplayServerOSX::KeyEvent ke; @@ -1507,6 +1513,14 @@ inline void sendPanEvent(DisplayServer::WindowID window_id, double dx, double dy deltaY *= 0.03; } + if ([event momentumPhase] != NSEventPhaseNone) { + if (ignore_momentum_scroll) { + return; + } + } else { + ignore_momentum_scroll = false; + } + if ([event phase] != NSEventPhaseNone || [event momentumPhase] != NSEventPhaseNone) { sendPanEvent(window_id, deltaX, deltaY, [event modifierFlags]); } else { diff --git a/scene/gui/color_rect.cpp b/scene/gui/color_rect.cpp index 627e589c02..0c38b93c60 100644 --- a/scene/gui/color_rect.cpp +++ b/scene/gui/color_rect.cpp @@ -30,12 +30,12 @@ #include "color_rect.h" -void ColorRect::set_frame_color(const Color &p_color) { +void ColorRect::set_color(const Color &p_color) { color = p_color; update(); } -Color ColorRect::get_frame_color() const { +Color ColorRect::get_color() const { return color; } @@ -46,12 +46,8 @@ void ColorRect::_notification(int p_what) { } void ColorRect::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_frame_color", "color"), &ColorRect::set_frame_color); - ClassDB::bind_method(D_METHOD("get_frame_color"), &ColorRect::get_frame_color); + ClassDB::bind_method(D_METHOD("set_color", "color"), &ColorRect::set_color); + ClassDB::bind_method(D_METHOD("get_color"), &ColorRect::get_color); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_frame_color", "get_frame_color"); -} - -ColorRect::ColorRect() { - color = Color(1, 1, 1); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); } diff --git a/scene/gui/color_rect.h b/scene/gui/color_rect.h index 3df44b9334..61d57f7cca 100644 --- a/scene/gui/color_rect.h +++ b/scene/gui/color_rect.h @@ -36,17 +36,15 @@ class ColorRect : public Control { GDCLASS(ColorRect, Control); - Color color; + Color color = Color(1, 1, 1); protected: void _notification(int p_what); static void _bind_methods(); public: - void set_frame_color(const Color &p_color); - Color get_frame_color() const; - - ColorRect(); + void set_color(const Color &p_color); + Color get_color() const; }; #endif // COLOR_RECT_H diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index a7c15e7027..ad02aaade5 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -775,6 +775,11 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { } if (mm.is_valid() && dragging) { + if (!moving_selection) { + emit_signal("begin_node_move"); + moving_selection = true; + } + just_selected = true; drag_accum += mm->get_relative(); for (int i = get_child_count() - 1; i >= 0; i--) { @@ -881,16 +886,17 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) { } if (drag_accum != Vector2()) { - emit_signal("_begin_node_move"); - for (int i = get_child_count() - 1; i >= 0; i--) { GraphNode *gn = Object::cast_to<GraphNode>(get_child(i)); if (gn && gn->is_selected()) { gn->set_drag(false); } } + } - emit_signal("_end_node_move"); + if (moving_selection) { + emit_signal("end_node_move"); + moving_selection = false; } dragging = false; @@ -1281,8 +1287,8 @@ void GraphEdit::_bind_methods() { ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING_NAME, "from"), PropertyInfo(Variant::INT, "from_slot"), PropertyInfo(Variant::VECTOR2, "release_position"))); ADD_SIGNAL(MethodInfo("connection_from_empty", PropertyInfo(Variant::STRING_NAME, "to"), PropertyInfo(Variant::INT, "to_slot"), PropertyInfo(Variant::VECTOR2, "release_position"))); ADD_SIGNAL(MethodInfo("delete_nodes_request")); - ADD_SIGNAL(MethodInfo("_begin_node_move")); - ADD_SIGNAL(MethodInfo("_end_node_move")); + ADD_SIGNAL(MethodInfo("begin_node_move")); + ADD_SIGNAL(MethodInfo("end_node_move")); ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "ofs"))); } diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 37cb5989e9..d87bd41f27 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -98,6 +98,7 @@ private: bool dragging; bool just_selected; + bool moving_selection; Vector2 drag_accum; float zoom; diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 9b1738ec79..d65e98ea46 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -53,17 +53,9 @@ void MenuButton::_unhandled_key_input(Ref<InputEvent> p_event) { } void MenuButton::pressed() { - { - Window *w = Object::cast_to<Window>(get_viewport()); - if (w && !w->is_embedding_subwindows()) { - print_line("windowpos: " + w->get_position()); - } - } Size2 size = get_size(); Point2 gp = get_screen_position(); - - print_line("screenpos: " + gp); gp.y += get_size().y; popup->set_position(gp); diff --git a/scene/gui/nine_patch_rect.h b/scene/gui/nine_patch_rect.h index 487fe4c860..a539ad43c0 100644 --- a/scene/gui/nine_patch_rect.h +++ b/scene/gui/nine_patch_rect.h @@ -79,4 +79,5 @@ public: }; VARIANT_ENUM_CAST(NinePatchRect::AxisStretchMode) + #endif // NINE_PATCH_RECT_H diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 957e1c11c7..dc21980473 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -6406,7 +6406,7 @@ void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName &p_funct } void TextEdit::set_line(int line, String new_text) { - if (line < 0 || line > text.size()) { + if (line < 0 || line >= text.size()) { return; } _remove_text(line, 0, line, text[line].length()); diff --git a/scene/gui/texture_rect.h b/scene/gui/texture_rect.h index efd3f0698a..e39545f679 100644 --- a/scene/gui/texture_rect.h +++ b/scene/gui/texture_rect.h @@ -28,8 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef TEXTURE_FRAME_H -#define TEXTURE_FRAME_H +#ifndef TEXTURE_RECT_H +#define TEXTURE_RECT_H #include "scene/gui/control.h" @@ -83,4 +83,5 @@ public: }; VARIANT_ENUM_CAST(TextureRect::StretchMode); -#endif // TEXTURE_FRAME_H + +#endif // TEXTURE_RECT_H diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index adefb53862..ea0fe6fcc2 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1383,14 +1383,17 @@ SceneTree::SceneTree() { root->set_as_audio_listener_2d(true); current_scene = nullptr; - int msaa_mode = GLOBAL_DEF("rendering/quality/screen_filters/msaa", 0); + const int msaa_mode = GLOBAL_DEF("rendering/quality/screen_filters/msaa", 0); ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/screen_filters/msaa", PropertyInfo(Variant::INT, "rendering/quality/screen_filters/msaa", PROPERTY_HINT_ENUM, "Disabled (Fastest),2x (Fast),4x (Average),8x (Slow),16x (Slower)")); root->set_msaa(Viewport::MSAA(msaa_mode)); - int ssaa_mode = GLOBAL_DEF("rendering/quality/screen_filters/screen_space_aa", 0); + const int ssaa_mode = GLOBAL_DEF("rendering/quality/screen_filters/screen_space_aa", 0); ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/screen_filters/screen_space_aa", PropertyInfo(Variant::INT, "rendering/quality/screen_filters/screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)")); root->set_screen_space_aa(Viewport::ScreenSpaceAA(ssaa_mode)); + const bool use_debanding = GLOBAL_DEF("rendering/quality/screen_filters/use_debanding", false); + root->set_use_debanding(use_debanding); + { //load default fallback environment //get possible extensions List<String> exts; diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 3c7475a150..414e488244 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -3138,6 +3138,17 @@ Viewport::ScreenSpaceAA Viewport::get_screen_space_aa() const { return screen_space_aa; } +void Viewport::set_use_debanding(bool p_use_debanding) { + if (use_debanding == p_use_debanding) + return; + use_debanding = p_use_debanding; + RS::get_singleton()->viewport_set_use_debanding(viewport, p_use_debanding); +} + +bool Viewport::is_using_debanding() const { + return use_debanding; +} + void Viewport::set_debug_draw(DebugDraw p_debug_draw) { debug_draw = p_debug_draw; RS::get_singleton()->viewport_set_debug_draw(viewport, RS::ViewportDebugDraw(p_debug_draw)); @@ -3324,6 +3335,9 @@ void Viewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_screen_space_aa", "screen_space_aa"), &Viewport::set_screen_space_aa); ClassDB::bind_method(D_METHOD("get_screen_space_aa"), &Viewport::get_screen_space_aa); + ClassDB::bind_method(D_METHOD("set_use_debanding", "enable"), &Viewport::set_use_debanding); + ClassDB::bind_method(D_METHOD("is_using_debanding"), &Viewport::is_using_debanding); + ClassDB::bind_method(D_METHOD("set_debug_draw", "debug_draw"), &Viewport::set_debug_draw); ClassDB::bind_method(D_METHOD("get_debug_draw"), &Viewport::get_debug_draw); @@ -3397,6 +3411,7 @@ void Viewport::_bind_methods() { ADD_GROUP("Rendering", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x,AndroidVR 2x,AndroidVR 4x"), "set_msaa", "get_msaa"); ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled,FXAA"), "set_screen_space_aa", "get_screen_space_aa"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding"); ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw"); ADD_GROUP("Canvas Items", "canvas_item_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,MipmapLinear,MipmapNearest"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter"); diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 878ac47bca..f7457f8ab2 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -279,6 +279,7 @@ private: MSAA msaa; ScreenSpaceAA screen_space_aa; + bool use_debanding; Ref<ViewportTexture> default_texture; Set<ViewportTexture *> viewport_textures; @@ -521,6 +522,9 @@ public: void set_screen_space_aa(ScreenSpaceAA p_screen_space_aa); ScreenSpaceAA get_screen_space_aa() const; + void set_use_debanding(bool p_use_debanding); + bool is_using_debanding() const; + Vector2 get_camera_coords(const Vector2 &p_viewport_coords) const; Vector2 get_camera_rect_size() const; diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h index e64c517a0b..cecd313fbd 100644 --- a/servers/rendering/rasterizer.h +++ b/servers/rendering/rasterizer.h @@ -306,7 +306,7 @@ public: virtual void set_debug_draw_mode(RS::ViewportDebugDraw p_debug_draw) = 0; virtual RID render_buffers_create() = 0; - virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa) = 0; + virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) = 0; virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_limit) = 0; virtual bool screen_space_roughness_limiter_is_active() const = 0; diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp index 0a3a863ee7..409cfdfecf 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp @@ -700,6 +700,7 @@ void RasterizerEffectsRD::tonemapper(RID p_source_color, RID p_dst_framebuffer, tonemap.push_constant.use_color_correction = p_settings.use_color_correction; tonemap.push_constant.use_fxaa = p_settings.use_fxaa; + tonemap.push_constant.use_debanding = p_settings.use_debanding; tonemap.push_constant.pixel_size[0] = 1.0 / p_settings.texture_size.x; tonemap.push_constant.pixel_size[1] = 1.0 / p_settings.texture_size.y; diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h index 8607a6ee67..679263fbf6 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h @@ -188,7 +188,7 @@ class RasterizerEffectsRD { float pixel_size[2]; uint32_t use_fxaa; - uint32_t pad; + uint32_t use_debanding; }; /* tonemap actually writes to a framebuffer, which is @@ -651,6 +651,7 @@ public: RID color_correction_texture; bool use_fxaa = false; + bool use_debanding = false; Vector2i texture_size; }; diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp index 9e6225a97a..f708b6ce00 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp @@ -1173,6 +1173,94 @@ void RasterizerSceneRD::sdfgi_update_probes(RID p_render_buffers, RID p_environm /* Update dynamic lights */ { + int32_t cascade_light_count[SDFGI::MAX_CASCADES]; + + for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) { + SDFGI::Cascade &cascade = rb->sdfgi->cascades[i]; + + SDGIShader::Light lights[SDFGI::MAX_DYNAMIC_LIGHTS]; + uint32_t idx = 0; + for (uint32_t j = 0; j < p_directional_light_count; j++) { + if (idx == SDFGI::MAX_DYNAMIC_LIGHTS) { + break; + } + + LightInstance *li = light_instance_owner.getornull(p_directional_light_instances[j]); + ERR_CONTINUE(!li); + Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z); + dir.y *= rb->sdfgi->y_mult; + dir.normalize(); + lights[idx].direction[0] = dir.x; + lights[idx].direction[1] = dir.y; + lights[idx].direction[2] = dir.z; + Color color = storage->light_get_color(li->light); + color = color.to_linear(); + lights[idx].color[0] = color.r; + lights[idx].color[1] = color.g; + lights[idx].color[2] = color.b; + lights[idx].type = RS::LIGHT_DIRECTIONAL; + lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY); + lights[idx].has_shadow = storage->light_has_shadow(li->light); + + idx++; + } + + AABB cascade_aabb; + cascade_aabb.position = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + cascade.position)) * cascade.cell_size; + cascade_aabb.size = Vector3(1, 1, 1) * rb->sdfgi->cascade_size * cascade.cell_size; + + for (uint32_t j = 0; j < p_positional_light_count; j++) { + if (idx == SDFGI::MAX_DYNAMIC_LIGHTS) { + break; + } + + LightInstance *li = light_instance_owner.getornull(p_positional_light_instances[j]); + ERR_CONTINUE(!li); + + uint32_t max_sdfgi_cascade = storage->light_get_max_sdfgi_cascade(li->light); + if (i > max_sdfgi_cascade) { + continue; + } + + if (!cascade_aabb.intersects(li->aabb)) { + continue; + } + + Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z); + //faster to not do this here + //dir.y *= rb->sdfgi->y_mult; + //dir.normalize(); + lights[idx].direction[0] = dir.x; + lights[idx].direction[1] = dir.y; + lights[idx].direction[2] = dir.z; + Vector3 pos = li->transform.origin; + pos.y *= rb->sdfgi->y_mult; + lights[idx].position[0] = pos.x; + lights[idx].position[1] = pos.y; + lights[idx].position[2] = pos.z; + Color color = storage->light_get_color(li->light); + color = color.to_linear(); + lights[idx].color[0] = color.r; + lights[idx].color[1] = color.g; + lights[idx].color[2] = color.b; + lights[idx].type = storage->light_get_type(li->light); + lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY); + lights[idx].has_shadow = storage->light_has_shadow(li->light); + lights[idx].attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION); + lights[idx].radius = storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE); + lights[idx].spot_angle = Math::deg2rad(storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ANGLE)); + lights[idx].spot_attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ATTENUATION); + + idx++; + } + + if (idx > 0) { + RD::get_singleton()->buffer_update(cascade.lights_buffer, 0, idx * sizeof(SDGIShader::Light), lights, true); + } + + cascade_light_count[i] = idx; + } + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.direct_light_pipeline[SDGIShader::DIRECT_LIGHT_MODE_DYNAMIC]); @@ -1191,91 +1279,7 @@ void RasterizerSceneRD::sdfgi_update_probes(RID p_render_buffers, RID p_environm for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) { SDFGI::Cascade &cascade = rb->sdfgi->cascades[i]; - - { //fill light buffer - - SDGIShader::Light lights[SDFGI::MAX_DYNAMIC_LIGHTS]; - uint32_t idx = 0; - for (uint32_t j = 0; j < p_directional_light_count; j++) { - if (idx == SDFGI::MAX_DYNAMIC_LIGHTS) { - break; - } - - LightInstance *li = light_instance_owner.getornull(p_directional_light_instances[j]); - ERR_CONTINUE(!li); - Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z); - dir.y *= rb->sdfgi->y_mult; - dir.normalize(); - lights[idx].direction[0] = dir.x; - lights[idx].direction[1] = dir.y; - lights[idx].direction[2] = dir.z; - Color color = storage->light_get_color(li->light); - color = color.to_linear(); - lights[idx].color[0] = color.r; - lights[idx].color[1] = color.g; - lights[idx].color[2] = color.b; - lights[idx].type = RS::LIGHT_DIRECTIONAL; - lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY); - lights[idx].has_shadow = storage->light_has_shadow(li->light); - - idx++; - } - - AABB cascade_aabb; - cascade_aabb.position = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + cascade.position)) * cascade.cell_size; - cascade_aabb.size = Vector3(1, 1, 1) * rb->sdfgi->cascade_size * cascade.cell_size; - - for (uint32_t j = 0; j < p_positional_light_count; j++) { - if (idx == SDFGI::MAX_DYNAMIC_LIGHTS) { - break; - } - - LightInstance *li = light_instance_owner.getornull(p_positional_light_instances[j]); - ERR_CONTINUE(!li); - - uint32_t max_sdfgi_cascade = storage->light_get_max_sdfgi_cascade(li->light); - if (i > max_sdfgi_cascade) { - continue; - } - - if (!cascade_aabb.intersects(li->aabb)) { - continue; - } - - Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z); - //faster to not do this here - //dir.y *= rb->sdfgi->y_mult; - //dir.normalize(); - lights[idx].direction[0] = dir.x; - lights[idx].direction[1] = dir.y; - lights[idx].direction[2] = dir.z; - Vector3 pos = li->transform.origin; - pos.y *= rb->sdfgi->y_mult; - lights[idx].position[0] = pos.x; - lights[idx].position[1] = pos.y; - lights[idx].position[2] = pos.z; - Color color = storage->light_get_color(li->light); - color = color.to_linear(); - lights[idx].color[0] = color.r; - lights[idx].color[1] = color.g; - lights[idx].color[2] = color.b; - lights[idx].type = storage->light_get_type(li->light); - lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY); - lights[idx].has_shadow = storage->light_has_shadow(li->light); - lights[idx].attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION); - lights[idx].radius = storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE); - lights[idx].spot_angle = Math::deg2rad(storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ANGLE)); - lights[idx].spot_attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ATTENUATION); - - idx++; - } - - if (idx > 0) { - RD::get_singleton()->buffer_update(cascade.lights_buffer, 0, idx * sizeof(SDGIShader::Light), lights, true); - } - push_constant.light_count = idx; - } - + push_constant.light_count = cascade_light_count[i]; push_constant.cascade = i; RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascade.sdf_direct_light_uniform_set, 0); @@ -5314,6 +5318,7 @@ void RasterizerSceneRD::_render_buffers_post_process_and_tonemap(RID p_render_bu tonemap.use_fxaa = true; } + tonemap.use_debanding = rb->use_debanding; tonemap.texture_size = Vector2i(rb->width, rb->height); if (env) { @@ -5693,13 +5698,14 @@ float RasterizerSceneRD::render_buffers_get_volumetric_fog_detail_spread(RID p_r return rb->volumetric_fog->spread; } -void RasterizerSceneRD::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa) { +void RasterizerSceneRD::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding) { RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); rb->width = p_width; rb->height = p_height; rb->render_target = p_render_target; rb->msaa = p_msaa; rb->screen_space_aa = p_screen_space_aa; + rb->use_debanding = p_use_debanding; _free_render_buffer_data(rb); { @@ -7234,8 +7240,9 @@ void RasterizerSceneRD::render_sdfgi(RID p_render_buffers, int p_region, Instanc push_constant.grid_size = rb->sdfgi->cascade_size; push_constant.cascade = cascade; - RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); if (rb->sdfgi->cascades[cascade].dirty_regions != SDFGI::Cascade::DIRTY_ALL) { + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + //must pre scroll existing data because not all is dirty RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_SCROLL]); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[cascade].scroll_uniform_set, 0); @@ -7309,13 +7316,15 @@ void RasterizerSceneRD::render_sdfgi(RID p_render_buffers, int p_region, Instanc } //ok finally barrier - RD::get_singleton()->compute_list_add_barrier(compute_list); + RD::get_singleton()->compute_list_end(); } //clear dispatch indirect data uint32_t dispatch_indirct_data[4] = { 0, 0, 0, 0 }; RD::get_singleton()->buffer_update(rb->sdfgi->cascades[cascade].solid_cell_dispatch_buffer, 0, sizeof(uint32_t) * 4, dispatch_indirct_data, true); + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + bool half_size = true; //much faster, very little difference static const int optimized_jf_group_size = 8; diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h index 8a14598250..2ad712a9d3 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h @@ -815,6 +815,7 @@ private: int width = 0, height = 0; RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED; RS::ViewportScreenSpaceAA screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; + bool use_debanding = false; RID render_target; @@ -1845,7 +1846,7 @@ public: } */ RID render_buffers_create(); - void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa); + void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa, bool p_use_debanding); RID render_buffers_get_ao_texture(RID p_render_buffers); RID render_buffers_get_back_buffer_texture(RID p_render_buffers); diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp index 90dd6af319..e723028e56 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp @@ -1256,6 +1256,10 @@ void RasterizerStorageRD::shader_set_code(RID p_shader, const String &p_code) { } material->shader_type = new_type; } + + for (Map<StringName, RID>::Element *E = shader->default_texture_parameter.front(); E; E = E->next()) { + shader->data->set_default_texture_param(E->key(), E->get()); + } } if (shader->data) { @@ -1292,7 +1296,9 @@ void RasterizerStorageRD::shader_set_default_texture_param(RID p_shader, const S } else { shader->default_texture_parameter.erase(p_name); } - + if (shader->data) { + shader->data->set_default_texture_param(p_name, p_texture); + } for (Set<Material *>::Element *E = shader->owners.front(); E; E = E->next()) { Material *material = E->get(); _material_queue_update(material, false, true); diff --git a/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp b/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp index f70ddbb75a..0a0c343e57 100644 --- a/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp +++ b/servers/rendering/rasterizer_rd/shader_compiler_rd.cpp @@ -423,13 +423,13 @@ static String _get_global_variable_from_type_and_index(const String &p_buffer, c return "(" + p_buffer + "[" + p_index + "].x != 0.0)"; } case ShaderLanguage::TYPE_BVEC2: { - return "(" + p_buffer + "[" + p_index + "].xy != vec2(0.0))"; + return "(notEqual(" + p_buffer + "[" + p_index + "].xy, vec2(0.0)))"; } case ShaderLanguage::TYPE_BVEC3: { - return "(" + p_buffer + "[" + p_index + "].xyz != vec3(0.0))"; + return "(notEqual(" + p_buffer + "[" + p_index + "].xyz, vec3(0.0)))"; } case ShaderLanguage::TYPE_BVEC4: { - return "(" + p_buffer + "[" + p_index + "].xyzw != vec4(0.0))"; + return "(notEqual(" + p_buffer + "[" + p_index + "].xyzw, vec4(0.0)))"; } case ShaderLanguage::TYPE_INT: { return "floatBitsToInt(" + p_buffer + "[" + p_index + "].x)"; @@ -444,16 +444,16 @@ static String _get_global_variable_from_type_and_index(const String &p_buffer, c return "floatBitsToInt(" + p_buffer + "[" + p_index + "].xyzw)"; } case ShaderLanguage::TYPE_UINT: { - return "floatBitsToUInt(" + p_buffer + "[" + p_index + "].x)"; + return "floatBitsToUint(" + p_buffer + "[" + p_index + "].x)"; } case ShaderLanguage::TYPE_UVEC2: { - return "floatBitsToUInt(" + p_buffer + "[" + p_index + "].xy)"; + return "floatBitsToUint(" + p_buffer + "[" + p_index + "].xy)"; } case ShaderLanguage::TYPE_UVEC3: { - return "floatBitsToUInt(" + p_buffer + "[" + p_index + "].xyz)"; + return "floatBitsToUint(" + p_buffer + "[" + p_index + "].xyz)"; } case ShaderLanguage::TYPE_UVEC4: { - return "floatBitsToUInt(" + p_buffer + "[" + p_index + "].xyzw)"; + return "floatBitsToUint(" + p_buffer + "[" + p_index + "].xyzw)"; } case ShaderLanguage::TYPE_FLOAT: { return "(" + p_buffer + "[" + p_index + "].x)"; @@ -1295,6 +1295,8 @@ void ShaderCompilerRD::initialize(DefaultIdentifierActions p_actions) { texture_functions.insert("textureLod"); texture_functions.insert("textureProjLod"); texture_functions.insert("textureGrad"); + texture_functions.insert("textureSize"); + texture_functions.insert("texelFetch"); } ShaderCompilerRD::ShaderCompilerRD() { diff --git a/servers/rendering/rasterizer_rd/shaders/tonemap.glsl b/servers/rendering/rasterizer_rd/shaders/tonemap.glsl index 74449496f6..341cdab1ef 100644 --- a/servers/rendering/rasterizer_rd/shaders/tonemap.glsl +++ b/servers/rendering/rasterizer_rd/shaders/tonemap.glsl @@ -48,7 +48,7 @@ layout(push_constant, binding = 1, std430) uniform Params { vec2 pixel_size; bool use_fxaa; - uint pad; + bool use_debanding; } params; @@ -157,6 +157,10 @@ vec3 tonemap_aces(vec3 color, float white) { } vec3 tonemap_reinhard(vec3 color, float white) { + // Ensure color values are positive. + // They can be negative in the case of negative lights, which leads to undesired behavior. + color = max(vec3(0.0), color); + return (white * color + color) / (color * white + white); } @@ -301,6 +305,18 @@ vec3 do_fxaa(vec3 color, float exposure, vec2 uv_interp) { } } +// From http://alex.vlachos.com/graphics/Alex_Vlachos_Advanced_VR_Rendering_GDC2015.pdf +// and https://www.shadertoy.com/view/MslGR8 (5th one starting from the bottom) +// NOTE: `frag_coord` is in pixels (i.e. not normalized UV). +vec3 screen_space_dither(vec2 frag_coord) { + // Iestyn's RGB dither (7 asm instructions) from Portal 2 X360, slightly modified for VR. + vec3 dither = vec3(dot(vec2(171.0, 231.0), frag_coord)); + dither.rgb = fract(dither.rgb / vec3(103.0, 71.0, 97.0)); + + // Subtract 0.5 to avoid slightly brightening the whole viewport. + return (dither.rgb - 0.5) / 255.0; +} + void main() { vec3 color = textureLod(source_color, uv_interp, 0.0f).rgb; @@ -324,6 +340,11 @@ void main() { if (params.use_fxaa) { color = do_fxaa(color, exposure, uv_interp); } + if (params.use_debanding) { + // For best results, debanding should be done before tonemapping. + // Otherwise, we're adding noise to an already-quantized image. + color += screen_space_dither(gl_FragCoord.xy); + } color = apply_tonemapping(color, params.white); color = linear_to_srgb(color); // regular linear -> SRGB conversion diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h index fb3baeca95..97477f1d3e 100644 --- a/servers/rendering/rendering_server_raster.h +++ b/servers/rendering/rendering_server_raster.h @@ -535,6 +535,7 @@ public: BIND3(viewport_set_shadow_atlas_quadrant_subdivision, RID, int, int) BIND2(viewport_set_msaa, RID, ViewportMSAA) BIND2(viewport_set_screen_space_aa, RID, ViewportScreenSpaceAA) + BIND2(viewport_set_use_debanding, RID, bool) BIND2R(int, viewport_get_render_info, RID, ViewportRenderInfo) BIND2(viewport_set_debug_draw, RID, ViewportDebugDraw) diff --git a/servers/rendering/rendering_server_viewport.cpp b/servers/rendering/rendering_server_viewport.cpp index 48be6ca13b..bd9aa32147 100644 --- a/servers/rendering/rendering_server_viewport.cpp +++ b/servers/rendering/rendering_server_viewport.cpp @@ -115,7 +115,7 @@ void RenderingServerViewport::_draw_viewport(Viewport *p_viewport, XRInterface:: if ((scenario_draw_canvas_bg || can_draw_3d) && !p_viewport->render_buffers.is_valid()) { //wants to draw 3D but there is no render buffer, create p_viewport->render_buffers = RSG::scene_render->render_buffers_create(); - RSG::scene_render->render_buffers_configure(p_viewport->render_buffers, p_viewport->render_target, p_viewport->size.width, p_viewport->size.height, p_viewport->msaa, p_viewport->screen_space_aa); + RSG::scene_render->render_buffers_configure(p_viewport->render_buffers, p_viewport->render_target, p_viewport->size.width, p_viewport->size.height, p_viewport->msaa, p_viewport->screen_space_aa, p_viewport->use_debanding); } RSG::storage->render_target_request_clear(p_viewport->render_target, bgcolor); @@ -491,7 +491,7 @@ void RenderingServerViewport::viewport_set_size(RID p_viewport, int p_width, int RSG::scene_render->free(viewport->render_buffers); viewport->render_buffers = RID(); } else { - RSG::scene_render->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa); + RSG::scene_render->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, viewport->use_debanding); } } } @@ -704,7 +704,7 @@ void RenderingServerViewport::viewport_set_msaa(RID p_viewport, RS::ViewportMSAA } viewport->msaa = p_msaa; if (viewport->render_buffers.is_valid()) { - RSG::scene_render->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, p_msaa, viewport->screen_space_aa); + RSG::scene_render->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, p_msaa, viewport->screen_space_aa, viewport->use_debanding); } } @@ -717,7 +717,20 @@ void RenderingServerViewport::viewport_set_screen_space_aa(RID p_viewport, RS::V } viewport->screen_space_aa = p_mode; if (viewport->render_buffers.is_valid()) { - RSG::scene_render->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, p_mode); + RSG::scene_render->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, p_mode, viewport->use_debanding); + } +} + +void RenderingServerViewport::viewport_set_use_debanding(RID p_viewport, bool p_use_debanding) { + Viewport *viewport = viewport_owner.getornull(p_viewport); + ERR_FAIL_COND(!viewport); + + if (viewport->use_debanding == p_use_debanding) { + return; + } + viewport->use_debanding = p_use_debanding; + if (viewport->render_buffers.is_valid()) { + RSG::scene_render->render_buffers_configure(viewport->render_buffers, viewport->render_target, viewport->size.width, viewport->size.height, viewport->msaa, viewport->screen_space_aa, p_use_debanding); } } diff --git a/servers/rendering/rendering_server_viewport.h b/servers/rendering/rendering_server_viewport.h index 0b90646e4f..ed251f5da0 100644 --- a/servers/rendering/rendering_server_viewport.h +++ b/servers/rendering/rendering_server_viewport.h @@ -59,6 +59,7 @@ public: RS::ViewportMSAA msaa; RS::ViewportScreenSpaceAA screen_space_aa; + bool use_debanding; DisplayServer::WindowID viewport_to_screen; Rect2 viewport_to_screen_rect; @@ -130,6 +131,7 @@ public: debug_draw = RS::VIEWPORT_DEBUG_DRAW_DISABLED; msaa = RS::VIEWPORT_MSAA_DISABLED; screen_space_aa = RS::VIEWPORT_SCREEN_SPACE_AA_DISABLED; + use_debanding = false; for (int i = 0; i < RS::VIEWPORT_RENDER_INFO_MAX; i++) { render_info[i] = 0; @@ -206,6 +208,7 @@ public: void viewport_set_msaa(RID p_viewport, RS::ViewportMSAA p_msaa); void viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode); + void viewport_set_use_debanding(RID p_viewport, bool p_use_debanding); virtual int viewport_get_render_info(RID p_viewport, RS::ViewportRenderInfo p_info); virtual void viewport_set_debug_draw(RID p_viewport, RS::ViewportDebugDraw p_draw); diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h index 305a3aaee7..c2337f8c9e 100644 --- a/servers/rendering/rendering_server_wrap_mt.h +++ b/servers/rendering/rendering_server_wrap_mt.h @@ -438,6 +438,7 @@ public: FUNC3(viewport_set_shadow_atlas_quadrant_subdivision, RID, int, int) FUNC2(viewport_set_msaa, RID, ViewportMSAA) FUNC2(viewport_set_screen_space_aa, RID, ViewportScreenSpaceAA) + FUNC2(viewport_set_use_debanding, RID, bool) //this passes directly to avoid stalling, but it's pretty dangerous, so don't call after freeing a viewport virtual int viewport_get_render_info(RID p_viewport, ViewportRenderInfo p_info) { diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index d747f31318..e2f1ddb224 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1734,6 +1734,8 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("viewport_set_shadow_atlas_size", "viewport", "size"), &RenderingServer::viewport_set_shadow_atlas_size); ClassDB::bind_method(D_METHOD("viewport_set_shadow_atlas_quadrant_subdivision", "viewport", "quadrant", "subdivision"), &RenderingServer::viewport_set_shadow_atlas_quadrant_subdivision); ClassDB::bind_method(D_METHOD("viewport_set_msaa", "viewport", "msaa"), &RenderingServer::viewport_set_msaa); + ClassDB::bind_method(D_METHOD("viewport_set_use_debanding", "viewport", "enable"), &RenderingServer::viewport_set_use_debanding); + ClassDB::bind_method(D_METHOD("viewport_get_render_info", "viewport", "info"), &RenderingServer::viewport_get_render_info); ClassDB::bind_method(D_METHOD("viewport_set_debug_draw", "viewport", "draw"), &RenderingServer::viewport_set_debug_draw); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 11c73c63d0..e50170bdf7 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -727,6 +727,8 @@ public: virtual void viewport_set_screen_space_aa(RID p_viewport, ViewportScreenSpaceAA p_mode) = 0; + virtual void viewport_set_use_debanding(RID p_viewport, bool p_use_debanding) = 0; + enum ViewportRenderInfo { VIEWPORT_RENDER_INFO_OBJECTS_IN_FRAME, VIEWPORT_RENDER_INFO_VERTICES_IN_FRAME, |