diff options
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/gles3/rasterizer_canvas_gles3.cpp | 45 | ||||
| -rw-r--r-- | drivers/metal/metal_objects.h | 267 | ||||
| -rw-r--r-- | drivers/metal/metal_objects.mm | 172 | ||||
| -rw-r--r-- | drivers/metal/rendering_context_driver_metal.mm | 6 | ||||
| -rw-r--r-- | drivers/metal/rendering_device_driver_metal.h | 8 | ||||
| -rw-r--r-- | drivers/metal/rendering_device_driver_metal.mm | 72 | ||||
| -rw-r--r-- | drivers/unix/file_access_unix.cpp | 41 | ||||
| -rw-r--r-- | drivers/unix/file_access_unix.h | 4 | ||||
| -rw-r--r-- | drivers/unix/net_socket_unix.cpp (renamed from drivers/unix/net_socket_posix.cpp) | 262 | ||||
| -rw-r--r-- | drivers/unix/net_socket_unix.h (renamed from drivers/unix/net_socket_posix.h) | 74 | ||||
| -rw-r--r-- | drivers/unix/os_unix.cpp | 2 | ||||
| -rw-r--r-- | drivers/vulkan/rendering_device_driver_vulkan.cpp | 22 | ||||
| -rw-r--r-- | drivers/vulkan/rendering_device_driver_vulkan.h | 1 | ||||
| -rw-r--r-- | drivers/windows/file_access_windows.cpp | 2 | ||||
| -rw-r--r-- | drivers/windows/net_socket_winsock.cpp | 613 | ||||
| -rw-r--r-- | drivers/windows/net_socket_winsock.h | 102 |
16 files changed, 1230 insertions, 463 deletions
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 3c959f0143..0138f99d50 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -1654,28 +1654,39 @@ void RasterizerCanvasGLES3::light_update_shadow(RID p_rid, int p_shadow_index, c return; } - for (int i = 0; i < 4; i++) { - glViewport((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2); + Projection projection; + { + real_t fov = 90; + real_t nearp = p_near; + real_t farp = p_far; + real_t aspect = 1.0; - Projection projection; - { - real_t fov = 90; - real_t nearp = p_near; - real_t farp = p_far; - real_t aspect = 1.0; + real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5)); + real_t ymin = -ymax; + real_t xmin = ymin * aspect; + real_t xmax = ymax * aspect; - real_t ymax = nearp * Math::tan(Math::deg_to_rad(fov * 0.5)); - real_t ymin = -ymax; - real_t xmin = ymin * aspect; - real_t xmax = ymax * aspect; + projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp); + } - projection.set_frustum(xmin, xmax, ymin, ymax, nearp, farp); - } + // Precomputed: + // Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0)); + // projection = projection * Projection(Transform3D().looking_at(cam_targets[i], Vector3(0, 0, -1)).affine_inverse()); + const Projection projections[4] = { + projection * Projection(Vector4(0, 0, -1, 0), Vector4(1, 0, 0, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)), - Vector3 cam_target = Basis::from_euler(Vector3(0, 0, Math_TAU * ((i + 3) / 4.0))).xform(Vector3(0, 1, 0)); + projection * Projection(Vector4(-1, 0, 0, 0), Vector4(0, 0, -1, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)), + + projection * Projection(Vector4(0, 0, 1, 0), Vector4(-1, 0, 0, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)), + + projection * Projection(Vector4(1, 0, 0, 0), Vector4(0, 0, 1, 0), Vector4(0, -1, 0, 0), Vector4(0, 0, 0, 1)) + + }; + + for (int i = 0; i < 4; i++) { + glViewport((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2); - projection = projection * Projection(Transform3D().looking_at(cam_target, Vector3(0, 0, -1)).affine_inverse()); - shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, projection, shadow_render.shader_version, variant); + shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::PROJECTION, projections[i], shadow_render.shader_version, variant); static const Vector2 directions[4] = { Vector2(1, 0), Vector2(0, 1), Vector2(-1, 0), Vector2(0, -1) }; shadow_render.shader.version_set_uniform(CanvasOcclusionShaderGLES3::DIRECTION, directions[i].x, directions[i].y, shadow_render.shader_version, variant); diff --git a/drivers/metal/metal_objects.h b/drivers/metal/metal_objects.h index fd7d93bbbd..5eda826d2a 100644 --- a/drivers/metal/metal_objects.h +++ b/drivers/metal/metal_objects.h @@ -82,6 +82,9 @@ MTL_CLASS(Texture) } //namespace MTL +/// Metal buffer index for the view mask when rendering multi-view. +const uint32_t VIEW_MASK_BUFFER_INDEX = 24; + enum ShaderStageUsage : uint32_t { None = 0, Vertex = RDD::SHADER_STAGE_VERTEX_BIT, @@ -142,6 +145,12 @@ struct ClearAttKey { const static uint32_t STENCIL_INDEX = DEPTH_INDEX + 1; const static uint32_t ATTACHMENT_COUNT = STENCIL_INDEX + 1; + enum Flags : uint16_t { + CLEAR_FLAGS_NONE = 0, + CLEAR_FLAGS_LAYERED = 1 << 0, + }; + + Flags flags = CLEAR_FLAGS_NONE; uint16_t sample_count = 0; uint16_t pixel_formats[ATTACHMENT_COUNT] = { 0 }; @@ -150,19 +159,22 @@ struct ClearAttKey { _FORCE_INLINE_ void set_stencil_format(MTLPixelFormat p_fmt) { pixel_formats[STENCIL_INDEX] = p_fmt; } _FORCE_INLINE_ MTLPixelFormat depth_format() const { return (MTLPixelFormat)pixel_formats[DEPTH_INDEX]; } _FORCE_INLINE_ MTLPixelFormat stencil_format() const { return (MTLPixelFormat)pixel_formats[STENCIL_INDEX]; } + _FORCE_INLINE_ void enable_layered_rendering() { flags::set(flags, CLEAR_FLAGS_LAYERED); } _FORCE_INLINE_ bool is_enabled(uint32_t p_idx) const { return pixel_formats[p_idx] != 0; } _FORCE_INLINE_ bool is_depth_enabled() const { return pixel_formats[DEPTH_INDEX] != 0; } _FORCE_INLINE_ bool is_stencil_enabled() const { return pixel_formats[STENCIL_INDEX] != 0; } + _FORCE_INLINE_ bool is_layered_rendering_enabled() const { return flags::any(flags, CLEAR_FLAGS_LAYERED); } _FORCE_INLINE_ bool operator==(const ClearAttKey &p_rhs) const { return memcmp(this, &p_rhs, sizeof(ClearAttKey)) == 0; } uint32_t hash() const { - uint32_t h = hash_murmur3_one_32(sample_count); + uint32_t h = hash_murmur3_one_32(flags); + h = hash_murmur3_one_32(sample_count, h); h = hash_murmur3_buffer(pixel_formats, ATTACHMENT_COUNT * sizeof(pixel_formats[0]), h); - return h; + return hash_fmix32(h); } }; @@ -206,6 +218,97 @@ public: ~MDResourceCache() = default; }; +enum class MDAttachmentType : uint8_t { + None = 0, + Color = 1 << 0, + Depth = 1 << 1, + Stencil = 1 << 2, +}; + +_FORCE_INLINE_ MDAttachmentType &operator|=(MDAttachmentType &p_a, MDAttachmentType p_b) { + flags::set(p_a, p_b); + return p_a; +} + +_FORCE_INLINE_ bool operator&(MDAttachmentType p_a, MDAttachmentType p_b) { + return uint8_t(p_a) & uint8_t(p_b); +} + +struct MDSubpass { + uint32_t subpass_index = 0; + uint32_t view_count = 0; + LocalVector<RDD::AttachmentReference> input_references; + LocalVector<RDD::AttachmentReference> color_references; + RDD::AttachmentReference depth_stencil_reference; + LocalVector<RDD::AttachmentReference> resolve_references; + + MTLFmtCaps getRequiredFmtCapsForAttachmentAt(uint32_t p_index) const; +}; + +struct API_AVAILABLE(macos(11.0), ios(14.0)) MDAttachment { +private: + uint32_t index = 0; + uint32_t firstUseSubpassIndex = 0; + uint32_t lastUseSubpassIndex = 0; + +public: + MTLPixelFormat format = MTLPixelFormatInvalid; + MDAttachmentType type = MDAttachmentType::None; + MTLLoadAction loadAction = MTLLoadActionDontCare; + MTLStoreAction storeAction = MTLStoreActionDontCare; + MTLLoadAction stencilLoadAction = MTLLoadActionDontCare; + MTLStoreAction stencilStoreAction = MTLStoreActionDontCare; + uint32_t samples = 1; + + /*! + * @brief Returns true if this attachment is first used in the given subpass. + * @param p_subpass + * @return + */ + _FORCE_INLINE_ bool isFirstUseOf(MDSubpass const &p_subpass) const { + return p_subpass.subpass_index == firstUseSubpassIndex; + } + + /*! + * @brief Returns true if this attachment is last used in the given subpass. + * @param p_subpass + * @return + */ + _FORCE_INLINE_ bool isLastUseOf(MDSubpass const &p_subpass) const { + return p_subpass.subpass_index == lastUseSubpassIndex; + } + + void linkToSubpass(MDRenderPass const &p_pass); + + MTLStoreAction getMTLStoreAction(MDSubpass const &p_subpass, + bool p_is_rendering_entire_area, + bool p_has_resolve, + bool p_can_resolve, + bool p_is_stencil) const; + bool configureDescriptor(MTLRenderPassAttachmentDescriptor *p_desc, + PixelFormats &p_pf, + MDSubpass const &p_subpass, + id<MTLTexture> p_attachment, + bool p_is_rendering_entire_area, + bool p_has_resolve, + bool p_can_resolve, + bool p_is_stencil) const; + /** Returns whether this attachment should be cleared in the subpass. */ + bool shouldClear(MDSubpass const &p_subpass, bool p_is_stencil) const; +}; + +class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderPass { +public: + Vector<MDAttachment> attachments; + Vector<MDSubpass> subpasses; + + uint32_t get_sample_count() const { + return attachments.is_empty() ? 1 : attachments[0].samples; + } + + MDRenderPass(Vector<MDAttachment> &p_attachments, Vector<MDSubpass> &p_subpasses); +}; + class API_AVAILABLE(macos(11.0), ios(14.0)) MDCommandBuffer { private: RenderingDeviceDriverMetal *device_driver = nullptr; @@ -220,8 +323,8 @@ private: void _render_set_dirty_state(); void _render_bind_uniform_sets(); - static void _populate_vertices(simd::float4 *p_vertices, Size2i p_fb_size, VectorView<Rect2i> p_rects); - static uint32_t _populate_vertices(simd::float4 *p_vertices, uint32_t p_index, Rect2i const &p_rect, Size2i p_fb_size); + void _populate_vertices(simd::float4 *p_vertices, Size2i p_fb_size, VectorView<Rect2i> p_rects); + uint32_t _populate_vertices(simd::float4 *p_vertices, uint32_t p_index, Rect2i const &p_rect, Size2i p_fb_size); void _end_render_pass(); void _render_clear_render_area(); @@ -268,34 +371,14 @@ public: // Bit mask of the uniform sets that are dirty, to prevent redundant binding. uint64_t uniform_set_mask = 0; - _FORCE_INLINE_ void reset() { - pass = nil; - frameBuffer = nil; - pipeline = nil; - current_subpass = UINT32_MAX; - render_area = {}; - is_rendering_entire_area = false; - desc = nil; - encoder = nil; - index_buffer = nil; - index_type = MTLIndexTypeUInt16; - dirty = DIRTY_NONE; - uniform_sets.clear(); - uniform_set_mask = 0; - clear_values.clear(); - viewports.clear(); - scissors.clear(); - blend_constants.reset(); - vertex_buffers.clear(); - vertex_offsets.clear(); - // Keep the keys, as they are likely to be used again. - for (KeyValue<StageResourceUsage, LocalVector<__unsafe_unretained id<MTLResource>>> &kv : resource_usage) { - kv.value.clear(); - } - } - + _FORCE_INLINE_ void reset(); void end_encoding(); + _ALWAYS_INLINE_ const MDSubpass &get_subpass() const { + DEV_ASSERT(pass != nullptr); + return pass->subpasses[current_subpass]; + } + _FORCE_INLINE_ void mark_viewport_dirty() { if (viewports.is_empty()) { return; @@ -649,6 +732,7 @@ public: uint32_t size = 0; } frag; } push_constants; + bool needs_view_mask_buffer = false; MDLibrary *vert; MDLibrary *frag; @@ -659,7 +743,10 @@ public: void encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) final; - MDRenderShader(CharString p_name, Vector<UniformSet> p_sets, MDLibrary *p_vert, MDLibrary *p_frag); + MDRenderShader(CharString p_name, + bool p_needs_view_mask_buffer, + Vector<UniformSet> p_sets, + MDLibrary *p_vert, MDLibrary *p_frag); }; _FORCE_INLINE_ StageResourceUsage &operator|=(StageResourceUsage &p_a, uint32_t p_b) { @@ -702,96 +789,6 @@ public: BoundUniformSet &boundUniformSetForShader(MDShader *p_shader, id<MTLDevice> p_device); }; -enum class MDAttachmentType : uint8_t { - None = 0, - Color = 1 << 0, - Depth = 1 << 1, - Stencil = 1 << 2, -}; - -_FORCE_INLINE_ MDAttachmentType &operator|=(MDAttachmentType &p_a, MDAttachmentType p_b) { - flags::set(p_a, p_b); - return p_a; -} - -_FORCE_INLINE_ bool operator&(MDAttachmentType p_a, MDAttachmentType p_b) { - return uint8_t(p_a) & uint8_t(p_b); -} - -struct MDSubpass { - uint32_t subpass_index = 0; - LocalVector<RDD::AttachmentReference> input_references; - LocalVector<RDD::AttachmentReference> color_references; - RDD::AttachmentReference depth_stencil_reference; - LocalVector<RDD::AttachmentReference> resolve_references; - - MTLFmtCaps getRequiredFmtCapsForAttachmentAt(uint32_t p_index) const; -}; - -struct API_AVAILABLE(macos(11.0), ios(14.0)) MDAttachment { -private: - uint32_t index = 0; - uint32_t firstUseSubpassIndex = 0; - uint32_t lastUseSubpassIndex = 0; - -public: - MTLPixelFormat format = MTLPixelFormatInvalid; - MDAttachmentType type = MDAttachmentType::None; - MTLLoadAction loadAction = MTLLoadActionDontCare; - MTLStoreAction storeAction = MTLStoreActionDontCare; - MTLLoadAction stencilLoadAction = MTLLoadActionDontCare; - MTLStoreAction stencilStoreAction = MTLStoreActionDontCare; - uint32_t samples = 1; - - /*! - * @brief Returns true if this attachment is first used in the given subpass. - * @param p_subpass - * @return - */ - _FORCE_INLINE_ bool isFirstUseOf(MDSubpass const &p_subpass) const { - return p_subpass.subpass_index == firstUseSubpassIndex; - } - - /*! - * @brief Returns true if this attachment is last used in the given subpass. - * @param p_subpass - * @return - */ - _FORCE_INLINE_ bool isLastUseOf(MDSubpass const &p_subpass) const { - return p_subpass.subpass_index == lastUseSubpassIndex; - } - - void linkToSubpass(MDRenderPass const &p_pass); - - MTLStoreAction getMTLStoreAction(MDSubpass const &p_subpass, - bool p_is_rendering_entire_area, - bool p_has_resolve, - bool p_can_resolve, - bool p_is_stencil) const; - bool configureDescriptor(MTLRenderPassAttachmentDescriptor *p_desc, - PixelFormats &p_pf, - MDSubpass const &p_subpass, - id<MTLTexture> p_attachment, - bool p_is_rendering_entire_area, - bool p_has_resolve, - bool p_can_resolve, - bool p_is_stencil) const; - /** Returns whether this attachment should be cleared in the subpass. */ - bool shouldClear(MDSubpass const &p_subpass, bool p_is_stencil) const; -}; - -class API_AVAILABLE(macos(11.0), ios(14.0)) MDRenderPass { -public: - Vector<MDAttachment> attachments; - Vector<MDSubpass> subpasses; - - uint32_t get_sample_count() const { - return attachments.is_empty() ? 1 : attachments[0].samples; - } - - MDRenderPass(Vector<MDAttachment> &p_attachments, Vector<MDSubpass> &p_subpasses); -}; - class API_AVAILABLE(macos(11.0), ios(14.0)) MDPipeline { public: MDPipelineType type; @@ -892,13 +889,39 @@ public: }; class API_AVAILABLE(macos(11.0), ios(14.0)) MDFrameBuffer { -public: Vector<MTL::Texture> textures; + +public: Size2i size; MDFrameBuffer(Vector<MTL::Texture> p_textures, Size2i p_size) : textures(p_textures), size(p_size) {} MDFrameBuffer() {} + /// Returns the texture at the given index. + _ALWAYS_INLINE_ MTL::Texture get_texture(uint32_t p_idx) const { + return textures[p_idx]; + } + + /// Returns true if the texture at the given index is not nil. + _ALWAYS_INLINE_ bool has_texture(uint32_t p_idx) const { + return textures[p_idx] != nil; + } + + /// Set the texture at the given index. + _ALWAYS_INLINE_ void set_texture(uint32_t p_idx, MTL::Texture p_texture) { + textures.write[p_idx] = p_texture; + } + + /// Unset or nil the texture at the given index. + _ALWAYS_INLINE_ void unset_texture(uint32_t p_idx) { + textures.write[p_idx] = nil; + } + + /// Resizes buffers to the specified size. + _ALWAYS_INLINE_ void set_texture_count(uint32_t p_size) { + textures.resize(p_size); + } + virtual ~MDFrameBuffer() = default; }; diff --git a/drivers/metal/metal_objects.mm b/drivers/metal/metal_objects.mm index c3906af159..11ab209d60 100644 --- a/drivers/metal/metal_objects.mm +++ b/drivers/metal/metal_objects.mm @@ -96,6 +96,9 @@ void MDCommandBuffer::bind_pipeline(RDD::PipelineID p_pipeline) { MDRenderPipeline *rp = (MDRenderPipeline *)p; if (render.encoder == nil) { + // This error would happen if the render pass failed. + ERR_FAIL_NULL_MSG(render.desc, "Render pass descriptor is null."); + // This condition occurs when there are no attachments when calling render_next_subpass() // and is due to the SUPPORTS_FRAGMENT_SHADER_WITH_ONLY_SIDE_EFFECTS flag. render.desc.defaultRasterSampleCount = static_cast<NSUInteger>(rp->sample_count); @@ -223,8 +226,9 @@ void MDCommandBuffer::render_bind_uniform_set(RDD::UniformSetID p_uniform_set, R void MDCommandBuffer::render_clear_attachments(VectorView<RDD::AttachmentClear> p_attachment_clears, VectorView<Rect2i> p_rects) { DEV_ASSERT(type == MDCommandBufferStateType::Render); - uint32_t vertex_count = p_rects.size() * 6; + const MDSubpass &subpass = render.get_subpass(); + uint32_t vertex_count = p_rects.size() * 6 * subpass.view_count; simd::float4 vertices[vertex_count]; simd::float4 clear_colors[ClearAttKey::ATTACHMENT_COUNT]; @@ -235,6 +239,9 @@ void MDCommandBuffer::render_clear_attachments(VectorView<RDD::AttachmentClear> ClearAttKey key; key.sample_count = render.pass->get_sample_count(); + if (subpass.view_count > 1) { + key.enable_layered_rendering(); + } float depth_value = 0; uint32_t stencil_value = 0; @@ -245,7 +252,7 @@ void MDCommandBuffer::render_clear_attachments(VectorView<RDD::AttachmentClear> if (attClear.aspect.has_flag(RDD::TEXTURE_ASPECT_COLOR_BIT)) { attachment_index = attClear.color_attachment; } else { - attachment_index = render.pass->subpasses[render.current_subpass].depth_stencil_reference.attachment; + attachment_index = subpass.depth_stencil_reference.attachment; } MDAttachment const &mda = render.pass->attachments[attachment_index]; @@ -310,6 +317,13 @@ void MDCommandBuffer::render_clear_attachments(VectorView<RDD::AttachmentClear> void MDCommandBuffer::_render_set_dirty_state() { _render_bind_uniform_sets(); + MDSubpass const &subpass = render.get_subpass(); + if (subpass.view_count > 1) { + uint32_t view_range[2] = { 0, subpass.view_count }; + [render.encoder setVertexBytes:view_range length:sizeof(view_range) atIndex:VIEW_MASK_BUFFER_INDEX]; + [render.encoder setFragmentBytes:view_range length:sizeof(view_range) atIndex:VIEW_MASK_BUFFER_INDEX]; + } + if (render.dirty.has_flag(RenderState::DIRTY_PIPELINE)) { [render.encoder setRenderPipelineState:render.pipeline->state]; } @@ -492,36 +506,40 @@ uint32_t MDCommandBuffer::_populate_vertices(simd::float4 *p_vertices, uint32_t simd::float4 vtx; uint32_t idx = p_index; - vtx.z = 0.0; - vtx.w = (float)1; + uint32_t endLayer = render.get_subpass().view_count; + + for (uint32_t layer = 0; layer < endLayer; layer++) { + vtx.z = 0.0; + vtx.w = (float)layer; - // Top left vertex - First triangle. - vtx.y = topPos; - vtx.x = leftPos; - p_vertices[idx++] = vtx; + // Top left vertex - First triangle. + vtx.y = topPos; + vtx.x = leftPos; + p_vertices[idx++] = vtx; - // Bottom left vertex. - vtx.y = bottomPos; - vtx.x = leftPos; - p_vertices[idx++] = vtx; + // Bottom left vertex. + vtx.y = bottomPos; + vtx.x = leftPos; + p_vertices[idx++] = vtx; - // Bottom right vertex. - vtx.y = bottomPos; - vtx.x = rightPos; - p_vertices[idx++] = vtx; + // Bottom right vertex. + vtx.y = bottomPos; + vtx.x = rightPos; + p_vertices[idx++] = vtx; - // Bottom right vertex - Second triangle. - p_vertices[idx++] = vtx; + // Bottom right vertex - Second triangle. + p_vertices[idx++] = vtx; - // Top right vertex. - vtx.y = topPos; - vtx.x = rightPos; - p_vertices[idx++] = vtx; + // Top right vertex. + vtx.y = topPos; + vtx.x = rightPos; + p_vertices[idx++] = vtx; - // Top left vertex. - vtx.y = topPos; - vtx.x = leftPos; - p_vertices[idx++] = vtx; + // Top left vertex. + vtx.y = topPos; + vtx.x = leftPos; + p_vertices[idx++] = vtx; + } return idx; } @@ -548,8 +566,7 @@ void MDCommandBuffer::render_begin_pass(RDD::RenderPassID p_render_pass, RDD::Fr void MDCommandBuffer::_end_render_pass() { MDFrameBuffer const &fb_info = *render.frameBuffer; - MDRenderPass const &pass_info = *render.pass; - MDSubpass const &subpass = pass_info.subpasses[render.current_subpass]; + MDSubpass const &subpass = render.get_subpass(); PixelFormats &pf = device_driver->get_pixel_formats(); @@ -557,11 +574,11 @@ void MDCommandBuffer::_end_render_pass() { uint32_t color_index = subpass.color_references[i].attachment; uint32_t resolve_index = subpass.resolve_references[i].attachment; DEV_ASSERT((color_index == RDD::AttachmentReference::UNUSED) == (resolve_index == RDD::AttachmentReference::UNUSED)); - if (color_index == RDD::AttachmentReference::UNUSED || !fb_info.textures[color_index]) { + if (color_index == RDD::AttachmentReference::UNUSED || !fb_info.has_texture(color_index)) { continue; } - id<MTLTexture> resolve_tex = fb_info.textures[resolve_index]; + id<MTLTexture> resolve_tex = fb_info.get_texture(resolve_index); CRASH_COND_MSG(!flags::all(pf.getCapabilities(resolve_tex.pixelFormat), kMTLFmtCapsResolve), "not implemented: unresolvable texture types"); // see: https://github.com/KhronosGroup/MoltenVK/blob/d20d13fe2735adb845636a81522df1b9d89c0fba/MoltenVK/MoltenVK/GPUObjects/MVKRenderPass.mm#L407 @@ -572,7 +589,7 @@ void MDCommandBuffer::_end_render_pass() { void MDCommandBuffer::_render_clear_render_area() { MDRenderPass const &pass = *render.pass; - MDSubpass const &subpass = pass.subpasses[render.current_subpass]; + MDSubpass const &subpass = render.get_subpass(); // First determine attachments that should be cleared. LocalVector<RDD::AttachmentClear> clears; @@ -619,9 +636,14 @@ void MDCommandBuffer::render_next_subpass() { MDFrameBuffer const &fb = *render.frameBuffer; MDRenderPass const &pass = *render.pass; - MDSubpass const &subpass = pass.subpasses[render.current_subpass]; + MDSubpass const &subpass = render.get_subpass(); MTLRenderPassDescriptor *desc = MTLRenderPassDescriptor.renderPassDescriptor; + + if (subpass.view_count > 1) { + desc.renderTargetArrayLength = subpass.view_count; + } + PixelFormats &pf = device_driver->get_pixel_formats(); uint32_t attachmentCount = 0; @@ -638,7 +660,7 @@ void MDCommandBuffer::render_next_subpass() { bool has_resolve = resolveIdx != RDD::AttachmentReference::UNUSED; bool can_resolve = true; if (resolveIdx != RDD::AttachmentReference::UNUSED) { - id<MTLTexture> resolve_tex = fb.textures[resolveIdx]; + id<MTLTexture> resolve_tex = fb.get_texture(resolveIdx); can_resolve = flags::all(pf.getCapabilities(resolve_tex.pixelFormat), kMTLFmtCapsResolve); if (can_resolve) { ca.resolveTexture = resolve_tex; @@ -649,7 +671,9 @@ void MDCommandBuffer::render_next_subpass() { MDAttachment const &attachment = pass.attachments[idx]; - id<MTLTexture> tex = fb.textures[idx]; + id<MTLTexture> tex = fb.get_texture(idx); + ERR_FAIL_NULL_MSG(tex, "Frame buffer color texture is null."); + if ((attachment.type & MDAttachmentType::Color)) { if (attachment.configureDescriptor(ca, pf, subpass, tex, render.is_rendering_entire_area, has_resolve, can_resolve, false)) { Color clearColor = render.clear_values[idx].color; @@ -662,7 +686,8 @@ void MDCommandBuffer::render_next_subpass() { attachmentCount += 1; uint32_t idx = subpass.depth_stencil_reference.attachment; MDAttachment const &attachment = pass.attachments[idx]; - id<MTLTexture> tex = fb.textures[idx]; + id<MTLTexture> tex = fb.get_texture(idx); + ERR_FAIL_NULL_MSG(tex, "Frame buffer depth / stencil texture is null."); if (attachment.type & MDAttachmentType::Depth) { MTLRenderPassDepthAttachmentDescriptor *da = desc.depthAttachment; if (attachment.configureDescriptor(da, pf, subpass, tex, render.is_rendering_entire_area, false, false, false)) { @@ -702,8 +727,15 @@ void MDCommandBuffer::render_draw(uint32_t p_vertex_count, uint32_t p_base_vertex, uint32_t p_first_instance) { DEV_ASSERT(type == MDCommandBufferStateType::Render); + ERR_FAIL_NULL_MSG(render.pipeline, "No pipeline set for render command buffer."); + _render_set_dirty_state(); + MDSubpass const &subpass = render.get_subpass(); + if (subpass.view_count > 1) { + p_instance_count *= subpass.view_count; + } + DEV_ASSERT(render.dirty == 0); id<MTLRenderCommandEncoder> enc = render.encoder; @@ -751,8 +783,15 @@ void MDCommandBuffer::render_draw_indexed(uint32_t p_index_count, int32_t p_vertex_offset, uint32_t p_first_instance) { DEV_ASSERT(type == MDCommandBufferStateType::Render); + ERR_FAIL_NULL_MSG(render.pipeline, "No pipeline set for render command buffer."); + _render_set_dirty_state(); + MDSubpass const &subpass = render.get_subpass(); + if (subpass.view_count > 1) { + p_instance_count *= subpass.view_count; + } + id<MTLRenderCommandEncoder> enc = render.encoder; uint32_t index_offset = render.index_offset; @@ -770,6 +809,8 @@ void MDCommandBuffer::render_draw_indexed(uint32_t p_index_count, void MDCommandBuffer::render_draw_indexed_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) { DEV_ASSERT(type == MDCommandBufferStateType::Render); + ERR_FAIL_NULL_MSG(render.pipeline, "No pipeline set for render command buffer."); + _render_set_dirty_state(); id<MTLRenderCommandEncoder> enc = render.encoder; @@ -794,6 +835,8 @@ void MDCommandBuffer::render_draw_indexed_indirect_count(RDD::BufferID p_indirec void MDCommandBuffer::render_draw_indirect(RDD::BufferID p_indirect_buffer, uint64_t p_offset, uint32_t p_draw_count, uint32_t p_stride) { DEV_ASSERT(type == MDCommandBufferStateType::Render); + ERR_FAIL_NULL_MSG(render.pipeline, "No pipeline set for render command buffer."); + _render_set_dirty_state(); id<MTLRenderCommandEncoder> enc = render.encoder; @@ -813,6 +856,42 @@ void MDCommandBuffer::render_draw_indirect_count(RDD::BufferID p_indirect_buffer ERR_FAIL_MSG("not implemented"); } +void MDCommandBuffer::render_end_pass() { + DEV_ASSERT(type == MDCommandBufferStateType::Render); + + render.end_encoding(); + render.reset(); + type = MDCommandBufferStateType::None; +} + +#pragma mark - RenderState + +void MDCommandBuffer::RenderState::reset() { + pass = nil; + frameBuffer = nil; + pipeline = nil; + current_subpass = UINT32_MAX; + render_area = {}; + is_rendering_entire_area = false; + desc = nil; + encoder = nil; + index_buffer = nil; + index_type = MTLIndexTypeUInt16; + dirty = DIRTY_NONE; + uniform_sets.clear(); + uniform_set_mask = 0; + clear_values.clear(); + viewports.clear(); + scissors.clear(); + blend_constants.reset(); + vertex_buffers.clear(); + vertex_offsets.clear(); + // Keep the keys, as they are likely to be used again. + for (KeyValue<StageResourceUsage, LocalVector<__unsafe_unretained id<MTLResource>>> &kv : resource_usage) { + kv.value.clear(); + } +} + void MDCommandBuffer::RenderState::end_encoding() { if (encoder == nil) { return; @@ -842,6 +921,8 @@ void MDCommandBuffer::RenderState::end_encoding() { encoder = nil; } +#pragma mark - ComputeState + void MDCommandBuffer::ComputeState::end_encoding() { if (encoder == nil) { return; @@ -862,14 +943,6 @@ void MDCommandBuffer::ComputeState::end_encoding() { encoder = nil; } -void MDCommandBuffer::render_end_pass() { - DEV_ASSERT(type == MDCommandBufferStateType::Render); - - render.end_encoding(); - render.reset(); - type = MDCommandBufferStateType::None; -} - #pragma mark - Compute void MDCommandBuffer::compute_bind_uniform_set(RDD::UniformSetID p_uniform_set, RDD::ShaderID p_shader, uint32_t p_set_index) { @@ -943,8 +1016,11 @@ void MDComputeShader::encode_push_constant_data(VectorView<uint32_t> p_data, MDC [enc setBytes:ptr length:length atIndex:push_constants.binding]; } -MDRenderShader::MDRenderShader(CharString p_name, Vector<UniformSet> p_sets, MDLibrary *_Nonnull p_vert, MDLibrary *_Nonnull p_frag) : - MDShader(p_name, p_sets), vert(p_vert), frag(p_frag) { +MDRenderShader::MDRenderShader(CharString p_name, + bool p_needs_view_mask_buffer, + Vector<UniformSet> p_sets, + MDLibrary *_Nonnull p_vert, MDLibrary *_Nonnull p_frag) : + MDShader(p_name, p_sets), needs_view_mask_buffer(p_needs_view_mask_buffer), vert(p_vert), frag(p_frag) { } void MDRenderShader::encode_push_constant_data(VectorView<uint32_t> p_data, MDCommandBuffer *p_cb) { @@ -1279,7 +1355,7 @@ typedef struct { typedef struct { float4 v_position [[position]]; - uint layer; + uint layer%s; } VaryingsPos; vertex VaryingsPos vertClear(AttributesPos attributes [[stage_in]], constant ClearColorsIn& ccIn [[buffer(0)]]) { @@ -1288,7 +1364,7 @@ vertex VaryingsPos vertClear(AttributesPos attributes [[stage_in]], constant Cle varyings.layer = uint(attributes.a_position.w); return varyings; } -)", ClearAttKey::DEPTH_INDEX]; +)", p_key.is_layered_rendering_enabled() ? " [[render_target_array_index]]" : "", ClearAttKey::DEPTH_INDEX]; return new_func(msl, @"vertClear", nil); } @@ -1578,7 +1654,7 @@ void ShaderCacheEntry::notify_free() const { self->_library = library; self->_error = error; if (error) { - ERR_PRINT(String(U"Error compiling shader %s: %s").format(entry->name.get_data(), error.localizedDescription.UTF8String)); + ERR_PRINT(vformat(U"Error compiling shader %s: %s", entry->name.get_data(), error.localizedDescription.UTF8String)); } { diff --git a/drivers/metal/rendering_context_driver_metal.mm b/drivers/metal/rendering_context_driver_metal.mm index b97b586352..cf8c7e1c83 100644 --- a/drivers/metal/rendering_context_driver_metal.mm +++ b/drivers/metal/rendering_context_driver_metal.mm @@ -134,7 +134,7 @@ public: frame_buffers.resize(p_desired_framebuffer_count); for (uint32_t i = 0; i < p_desired_framebuffer_count; i++) { // Reserve space for the drawable texture. - frame_buffers[i].textures.resize(1); + frame_buffers[i].set_texture_count(1); } return OK; @@ -154,7 +154,7 @@ public: id<CAMetalDrawable> drawable = layer.nextDrawable; ERR_FAIL_NULL_V_MSG(drawable, RDD::FramebufferID(), "no drawable available"); drawables[rear] = drawable; - frame_buffer.textures.write[0] = drawable.texture; + frame_buffer.set_texture(0, drawable.texture); return RDD::FramebufferID(&frame_buffer); } @@ -165,7 +165,7 @@ public: } // Release texture and drawable. - frame_buffers[front].textures.write[0] = nil; + frame_buffers[front].unset_texture(0); id<MTLDrawable> drawable = drawables[front]; drawables[front] = nil; diff --git a/drivers/metal/rendering_device_driver_metal.h b/drivers/metal/rendering_device_driver_metal.h index f62a164ef9..e238de958e 100644 --- a/drivers/metal/rendering_device_driver_metal.h +++ b/drivers/metal/rendering_device_driver_metal.h @@ -239,7 +239,13 @@ private: friend struct PushConstantData; private: - Error _reflect_spirv16(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection); + /// Contains additional metadata about the shader. + struct ShaderMeta { + /// Indicates whether the shader uses multiview. + bool has_multiview = false; + }; + + Error _reflect_spirv16(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection, ShaderMeta &r_shader_meta); public: virtual String shader_get_binary_cache_key() override final; diff --git a/drivers/metal/rendering_device_driver_metal.mm b/drivers/metal/rendering_device_driver_metal.mm index 4da11ecd21..d90f528a14 100644 --- a/drivers/metal/rendering_device_driver_metal.mm +++ b/drivers/metal/rendering_device_driver_metal.mm @@ -1026,7 +1026,7 @@ void RenderingDeviceDriverMetal::framebuffer_free(FramebufferID p_framebuffer) { #pragma mark - Shader -const uint32_t SHADER_BINARY_VERSION = 1; +const uint32_t SHADER_BINARY_VERSION = 2; // region Serialization @@ -1503,6 +1503,7 @@ struct API_AVAILABLE(macos(11.0), ios(14.0)) ShaderBinaryData { uint32_t fragment_output_mask = UINT32_MAX; uint32_t spirv_specialization_constants_ids_mask = UINT32_MAX; uint32_t is_compute = UINT32_MAX; + uint32_t needs_view_mask_buffer = UINT32_MAX; ComputeSize compute_local_size; PushConstantData push_constant; LocalVector<ShaderStageData> stages; @@ -1523,6 +1524,7 @@ struct API_AVAILABLE(macos(11.0), ios(14.0)) ShaderBinaryData { size += sizeof(uint32_t); // fragment_output_mask size += sizeof(uint32_t); // spirv_specialization_constants_ids_mask size += sizeof(uint32_t); // is_compute + size += sizeof(uint32_t); // needs_view_mask_buffer size += compute_local_size.serialize_size(); // compute_local_size size += push_constant.serialize_size(); // push_constant size += sizeof(uint32_t); // stages.size() @@ -1547,6 +1549,7 @@ struct API_AVAILABLE(macos(11.0), ios(14.0)) ShaderBinaryData { p_writer.write(fragment_output_mask); p_writer.write(spirv_specialization_constants_ids_mask); p_writer.write(is_compute); + p_writer.write(needs_view_mask_buffer); p_writer.write(compute_local_size); p_writer.write(push_constant); p_writer.write(VectorView(stages)); @@ -1561,6 +1564,7 @@ struct API_AVAILABLE(macos(11.0), ios(14.0)) ShaderBinaryData { p_reader.read(fragment_output_mask); p_reader.read(spirv_specialization_constants_ids_mask); p_reader.read(is_compute); + p_reader.read(needs_view_mask_buffer); p_reader.read(compute_local_size); p_reader.read(push_constant); p_reader.read(stages); @@ -1572,14 +1576,16 @@ struct API_AVAILABLE(macos(11.0), ios(14.0)) ShaderBinaryData { // endregion String RenderingDeviceDriverMetal::shader_get_binary_cache_key() { - return "Metal-SV" + uitos(SHADER_BINARY_VERSION); + static const String cache_key = "Metal-SV" + uitos(SHADER_BINARY_VERSION); + return cache_key; } -Error RenderingDeviceDriverMetal::_reflect_spirv16(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection) { +Error RenderingDeviceDriverMetal::_reflect_spirv16(VectorView<ShaderStageSPIRVData> p_spirv, ShaderReflection &r_reflection, ShaderMeta &r_shader_meta) { using namespace spirv_cross; using spirv_cross::Resource; r_reflection = {}; + r_shader_meta = {}; for (uint32_t i = 0; i < p_spirv.size(); i++) { ShaderStageSPIRVData const &v = p_spirv[i]; @@ -1811,6 +1817,20 @@ Error RenderingDeviceDriverMetal::_reflect_spirv16(VectorView<ShaderStageSPIRVDa } } + for (const BuiltInResource &res : resources.builtin_inputs) { + if (res.builtin == spv::BuiltInViewIndex || res.builtin == spv::BuiltInViewportIndex) { + r_shader_meta.has_multiview = true; + } + } + + if (!r_shader_meta.has_multiview) { + for (const BuiltInResource &res : resources.builtin_outputs) { + if (res.builtin == spv::BuiltInViewIndex || res.builtin == spv::BuiltInViewportIndex) { + r_shader_meta.has_multiview = true; + } + } + } + // Specialization constants. for (SpecializationConstant const &constant : compiler.get_specialization_constants()) { int32_t existing = -1; @@ -1874,7 +1894,8 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec using spirv_cross::Resource; ShaderReflection spirv_data; - ERR_FAIL_COND_V(_reflect_spirv16(p_spirv, spirv_data), Result()); + ShaderMeta shader_meta; + ERR_FAIL_COND_V(_reflect_spirv16(p_spirv, spirv_data, shader_meta), Result()); ShaderBinaryData bin_data{}; if (!p_shader_name.is_empty()) { @@ -1893,6 +1914,7 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec bin_data.is_compute = spirv_data.is_compute; bin_data.push_constant.size = spirv_data.push_constant_size; bin_data.push_constant.stages = (ShaderStageUsage)(uint8_t)spirv_data.push_constant_stages; + bin_data.needs_view_mask_buffer = shader_meta.has_multiview ? 1 : 0; for (uint32_t i = 0; i < spirv_data.uniform_sets.size(); i++) { const ::Vector<ShaderUniform> &spirv_set = spirv_data.uniform_sets[i]; @@ -1947,6 +1969,11 @@ Vector<uint8_t> RenderingDeviceDriverMetal::shader_compile_binary_from_spirv(Vec msl_options.pad_fragment_output_components = true; msl_options.r32ui_alignment_constant_id = R32UI_ALIGNMENT_CONSTANT_ID; msl_options.agx_manual_cube_grad_fixup = true; + if (shader_meta.has_multiview) { + msl_options.multiview = true; + msl_options.multiview_layered_rendering = true; + msl_options.view_mask_buffer_index = VIEW_MASK_BUFFER_INDEX; + } CompilerGLSL::Options options{}; options.vertex.flip_vert_y = true; @@ -2448,7 +2475,7 @@ RDD::ShaderID RenderingDeviceDriverMetal::shader_create_from_bytecode(const Vect #endif shader = cs; } else { - MDRenderShader *rs = new MDRenderShader(binary_data.shader_name, uniform_sets, libraries[ShaderStage::SHADER_STAGE_VERTEX], libraries[ShaderStage::SHADER_STAGE_FRAGMENT]); + MDRenderShader *rs = new MDRenderShader(binary_data.shader_name, (bool)binary_data.needs_view_mask_buffer, uniform_sets, libraries[ShaderStage::SHADER_STAGE_VERTEX], libraries[ShaderStage::SHADER_STAGE_FRAGMENT]); uint32_t *vert_binding = binary_data.push_constant.msl_binding.getptr(SHADER_STAGE_VERTEX); if (vert_binding) { @@ -2956,6 +2983,7 @@ RDD::RenderPassID RenderingDeviceDriverMetal::render_pass_create(VectorView<Atta for (uint32_t i = 0; i < subpass_count; i++) { MDSubpass &subpass = subpasses.write[i]; subpass.subpass_index = i; + subpass.view_count = p_view_count; subpass.input_references = p_subpasses[i].input_references; subpass.color_references = p_subpasses[i].color_references; subpass.depth_stencil_reference = p_subpasses[i].depth_stencil_reference; @@ -3675,8 +3703,7 @@ void RenderingDeviceDriverMetal::set_object_name(ObjectType p_type, ID p_driver_ uint64_t RenderingDeviceDriverMetal::get_resource_native_handle(DriverResource p_type, ID p_driver_id) { switch (p_type) { case DRIVER_RESOURCE_LOGICAL_DEVICE: { - uintptr_t devicePtr = (uintptr_t)(__bridge void *)device; - return (uint64_t)devicePtr; + return (uint64_t)(uintptr_t)(__bridge void *)device; } case DRIVER_RESOURCE_PHYSICAL_DEVICE: { return 0; @@ -3685,7 +3712,7 @@ uint64_t RenderingDeviceDriverMetal::get_resource_native_handle(DriverResource p return 0; } case DRIVER_RESOURCE_COMMAND_QUEUE: { - return 0; + return (uint64_t)(uintptr_t)(__bridge void *)device_queue; } case DRIVER_RESOURCE_QUEUE_FAMILY: { return 0; @@ -3702,15 +3729,20 @@ uint64_t RenderingDeviceDriverMetal::get_resource_native_handle(DriverResource p case DRIVER_RESOURCE_SAMPLER: { return p_driver_id.id; } - case DRIVER_RESOURCE_UNIFORM_SET: + case DRIVER_RESOURCE_UNIFORM_SET: { return 0; + } case DRIVER_RESOURCE_BUFFER: { return p_driver_id.id; } - case DRIVER_RESOURCE_COMPUTE_PIPELINE: - return 0; - case DRIVER_RESOURCE_RENDER_PIPELINE: - return 0; + case DRIVER_RESOURCE_COMPUTE_PIPELINE: { + MDComputePipeline *pipeline = (MDComputePipeline *)(p_driver_id.id); + return (uint64_t)(uintptr_t)(__bridge void *)pipeline->state; + } + case DRIVER_RESOURCE_RENDER_PIPELINE: { + MDRenderPipeline *pipeline = (MDRenderPipeline *)(p_driver_id.id); + return (uint64_t)(uintptr_t)(__bridge void *)pipeline->state; + } default: { return 0; } @@ -3842,7 +3874,7 @@ uint64_t RenderingDeviceDriverMetal::api_trait_get(ApiTrait p_trait) { bool RenderingDeviceDriverMetal::has_feature(Features p_feature) { switch (p_feature) { case SUPPORTS_MULTIVIEW: - return false; + return multiview_capabilities.is_supported; case SUPPORTS_FSR_HALF_FLOAT: return true; case SUPPORTS_ATTACHMENT_VRS: @@ -3951,6 +3983,18 @@ Error RenderingDeviceDriverMetal::initialize(uint32_t p_device_index, uint32_t p metal_device_properties = memnew(MetalDeviceProperties(device)); pixel_formats = memnew(PixelFormats(device)); + if (metal_device_properties->features.layeredRendering) { + multiview_capabilities.is_supported = true; + multiview_capabilities.max_view_count = metal_device_properties->limits.maxViewports; + // NOTE: I'm not sure what the limit is as I don't see it referenced anywhere + multiview_capabilities.max_instance_count = UINT32_MAX; + + print_verbose("- Metal multiview supported:"); + print_verbose(" max view count: " + itos(multiview_capabilities.max_view_count)); + print_verbose(" max instances: " + itos(multiview_capabilities.max_instance_count)); + } else { + print_verbose("- Metal multiview not supported"); + } // Check required features and abort if any of them is missing. if (!metal_device_properties->features.imageCubeArray) { diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp index 3d584341ed..43ad0799ba 100644 --- a/drivers/unix/file_access_unix.cpp +++ b/drivers/unix/file_access_unix.cpp @@ -41,6 +41,11 @@ #include <sys/types.h> #include <unistd.h> +#if defined(TOOLS_ENABLED) +#include <limits.h> +#include <stdlib.h> +#endif + void FileAccessUnix::check_errors() const { ERR_FAIL_NULL_MSG(f, "File must be opened before use."); @@ -87,6 +92,22 @@ Error FileAccessUnix::open_internal(const String &p_path, int p_mode_flags) { } } +#if defined(TOOLS_ENABLED) + if (p_mode_flags & READ) { + String real_path = get_real_path(); + if (real_path != path) { + // Don't warn on symlinks, since they can be used to simply share addons on multiple projects. + if (real_path.to_lower() == path.to_lower()) { + // The File system is case insensitive, but other platforms can be sensitive to it + // To ease cross-platform development, we issue a warning if users try to access + // a file using the wrong case (which *works* on Windows and macOS, but won't on other + // platforms). + WARN_PRINT(vformat("Case mismatch opening requested file '%s', stored as '%s' in the filesystem. This file will not open when exported to other case-sensitive platforms.", path, real_path)); + } + } + } +#endif + if (is_backup_save_enabled() && (p_mode_flags == WRITE)) { save_path = path; // Create a temporary file in the same directory as the target file. @@ -173,6 +194,26 @@ String FileAccessUnix::get_path_absolute() const { return path; } +#if defined(TOOLS_ENABLED) +String FileAccessUnix::get_real_path() const { + char *resolved_path = ::realpath(path.utf8().get_data(), nullptr); + + if (!resolved_path) { + return path; + } + + String result; + Error parse_ok = result.parse_utf8(resolved_path); + ::free(resolved_path); + + if (parse_ok != OK) { + return path; + } + + return result.simplify_path(); +} +#endif + void FileAccessUnix::seek(uint64_t p_position) { ERR_FAIL_NULL_MSG(f, "File must be opened before use."); diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h index 76f629f7c2..7caf8a14d7 100644 --- a/drivers/unix/file_access_unix.h +++ b/drivers/unix/file_access_unix.h @@ -51,6 +51,10 @@ class FileAccessUnix : public FileAccess { void _close(); +#if defined(TOOLS_ENABLED) + String get_real_path() const; // Returns the resolved real path for the current open file. +#endif + public: static CloseNotificationFunc close_notification_func; diff --git a/drivers/unix/net_socket_posix.cpp b/drivers/unix/net_socket_unix.cpp index 5caa33100e..3be615d9ad 100644 --- a/drivers/unix/net_socket_posix.cpp +++ b/drivers/unix/net_socket_unix.cpp @@ -1,5 +1,5 @@ /**************************************************************************/ -/* net_socket_posix.cpp */ +/* net_socket_unix.cpp */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,13 +28,11 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#include "net_socket_posix.h" - // Some proprietary Unix-derived platforms don't expose Unix sockets // so this allows skipping this file to reimplement this API differently. -#ifndef UNIX_SOCKET_UNAVAILABLE +#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE) -#if defined(UNIX_ENABLED) +#include "net_socket_unix.h" #include <errno.h> #include <fcntl.h> @@ -62,44 +60,11 @@ #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP #endif -// Some custom defines to minimize ifdefs -#define SOCK_EMPTY -1 -#define SOCK_BUF(x) x -#define SOCK_CBUF(x) x -#define SOCK_IOCTL ioctl -#define SOCK_FIONREAD_LEN_TYPE int -#define SOCK_CLOSE ::close -#define SOCK_CONNECT(p_sock, p_addr, p_addr_len) ::connect(p_sock, p_addr, p_addr_len) - -/* Windows */ -#elif defined(WINDOWS_ENABLED) -#include <winsock2.h> -#include <ws2tcpip.h> - -#include <mswsock.h> -// Some custom defines to minimize ifdefs -#define SOCK_EMPTY INVALID_SOCKET -#define SOCK_BUF(x) (char *)(x) -#define SOCK_CBUF(x) (const char *)(x) -#define SOCK_IOCTL ioctlsocket -#define SOCK_FIONREAD_LEN_TYPE unsigned long -#define SOCK_CLOSE closesocket -// connect is broken on windows under certain conditions, reasons unknown: -// See https://github.com/godotengine/webrtc-native/issues/6 -#define SOCK_CONNECT(p_sock, p_addr, p_addr_len) ::WSAConnect(p_sock, p_addr, p_addr_len, nullptr, nullptr, nullptr, nullptr) - -// Workaround missing flag in MinGW -#if defined(__MINGW32__) && !defined(SIO_UDP_NETRESET) -#define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15) -#endif - -#endif // UNIX_ENABLED - size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) { memset(p_addr, 0, sizeof(struct sockaddr_storage)); - if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket + if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket. - // IPv6 only socket with IPv4 address + // IPv6 only socket with IPv4 address. ERR_FAIL_COND_V(!p_ip.is_wildcard() && p_ip_type == IP::TYPE_IPV6 && p_ip.is_ipv4(), 0); struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; @@ -111,14 +76,14 @@ size_t NetSocketPosix::_set_addr_storage(struct sockaddr_storage *p_addr, const addr6->sin6_addr = in6addr_any; } return sizeof(sockaddr_in6); - } else { // IPv4 socket + } else { // IPv4 socket. - // IPv4 socket with IPv6 address + // IPv4 socket with IPv6 address. ERR_FAIL_COND_V(!p_ip.is_wildcard() && !p_ip.is_ipv4(), 0); struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; addr4->sin_family = AF_INET; - addr4->sin_port = htons(p_port); // short, network byte order + addr4->sin_port = htons(p_port); // Short, network byte order. if (p_ip.is_valid()) { memcpy(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4); @@ -155,26 +120,13 @@ NetSocket *NetSocketPosix::_create_func() { } void NetSocketPosix::make_default() { -#if defined(WINDOWS_ENABLED) - if (_create == nullptr) { - WSADATA data; - WSAStartup(MAKEWORD(2, 2), &data); - } -#endif _create = _create_func; } void NetSocketPosix::cleanup() { -#if defined(WINDOWS_ENABLED) - if (_create != nullptr) { - WSACleanup(); - } - _create = nullptr; -#endif } -NetSocketPosix::NetSocketPosix() : - _sock(SOCK_EMPTY) { +NetSocketPosix::NetSocketPosix() { } NetSocketPosix::~NetSocketPosix() { @@ -189,29 +141,6 @@ NetSocketPosix::~NetSocketPosix() { #endif NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const { -#if defined(WINDOWS_ENABLED) - int err = WSAGetLastError(); - if (err == WSAEISCONN) { - return ERR_NET_IS_CONNECTED; - } - if (err == WSAEINPROGRESS || err == WSAEALREADY) { - return ERR_NET_IN_PROGRESS; - } - if (err == WSAEWOULDBLOCK) { - return ERR_NET_WOULD_BLOCK; - } - if (err == WSAEADDRINUSE || err == WSAEADDRNOTAVAIL) { - return ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE; - } - if (err == WSAEACCES) { - return ERR_NET_UNAUTHORIZED; - } - if (err == WSAEMSGSIZE || err == WSAENOBUFS) { - return ERR_NET_BUFFER_TOO_SMALL; - } - print_verbose("Socket error: " + itos(err)); - return ERR_NET_OTHER; -#else if (errno == EISCONN) { return ERR_NET_IS_CONNECTED; } @@ -230,9 +159,8 @@ NetSocketPosix::NetError NetSocketPosix::_get_socket_error() const { if (errno == ENOBUFS) { return ERR_NET_BUFFER_TOO_SMALL; } - print_verbose("Socket error: " + itos(errno)); + print_verbose("Socket error: " + itos(errno) + "."); return ERR_NET_OTHER; -#endif } #if defined(__GNUC__) && !defined(__clang__) @@ -254,7 +182,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, Str ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); ERR_FAIL_COND_V(!_can_use_ip(p_ip, false), ERR_INVALID_PARAMETER); - // Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4 + // Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4. IP::Type type = _ip_type == IP::TYPE_ANY && p_ip.is_ipv4() ? IP::TYPE_IPV4 : _ip_type; // This needs to be the proper level for the multicast group, no matter if the socket is dual stacking. int level = type == IP::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6; @@ -277,7 +205,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, Str for (const IPAddress &F : c.ip_addresses) { if (!F.is_ipv4()) { - continue; // Wrong IP type + continue; // Wrong IP type. } if_ip = F; break; @@ -304,7 +232,7 @@ _FORCE_INLINE_ Error NetSocketPosix::_change_multicast_group(IPAddress p_ip, Str return OK; } -void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream) { +void NetSocketPosix::_set_socket(int p_sock, IP::Type p_ip_type, bool p_is_stream) { _sock = p_sock; _ip_type = p_ip_type; _is_stream = p_is_stream; @@ -313,11 +241,9 @@ void NetSocketPosix::_set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_ } void NetSocketPosix::_set_close_exec_enabled(bool p_enabled) { -#ifndef WINDOWS_ENABLED // Enable close on exec to avoid sharing with subprocesses. Off by default on Windows. int opts = fcntl(_sock, F_GETFD); fcntl(_sock, F_SETFD, opts | FD_CLOEXEC); -#endif } Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { @@ -336,7 +262,7 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { int type = p_sock_type == TYPE_TCP ? SOCK_STREAM : SOCK_DGRAM; _sock = socket(family, type, protocol); - if (_sock == SOCK_EMPTY && ip_type == IP::TYPE_ANY) { + if (_sock == -1 && ip_type == IP::TYPE_ANY) { // Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket // in place of a dual stack one, and further calls to _set_sock_addr will work as expected. ip_type = IP::TYPE_IPV4; @@ -344,11 +270,11 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { _sock = socket(family, type, protocol); } - ERR_FAIL_COND_V(_sock == SOCK_EMPTY, FAILED); + ERR_FAIL_COND_V(_sock == -1, FAILED); _ip_type = ip_type; if (family == AF_INET6) { - // Select IPv4 over IPv6 mapping + // Select IPv4 over IPv6 mapping. set_ipv6_only_enabled(ip_type != IP::TYPE_ANY); } @@ -363,36 +289,22 @@ Error NetSocketPosix::open(Type p_sock_type, IP::Type &ip_type) { // Disable descriptor sharing with subprocesses. _set_close_exec_enabled(true); -#if defined(WINDOWS_ENABLED) - if (!_is_stream) { - // Disable windows feature/bug reporting WSAECONNRESET/WSAENETRESET when - // recv/recvfrom and an ICMP reply was received from a previous send/sendto. - unsigned long disable = 0; - if (ioctlsocket(_sock, SIO_UDP_CONNRESET, &disable) == SOCKET_ERROR) { - print_verbose("Unable to turn off UDP WSAECONNRESET behavior on Windows"); - } - if (ioctlsocket(_sock, SIO_UDP_NETRESET, &disable) == SOCKET_ERROR) { - // This feature seems not to be supported on wine. - print_verbose("Unable to turn off UDP WSAENETRESET behavior on Windows"); - } - } -#endif #if defined(SO_NOSIGPIPE) - // Disable SIGPIPE (should only be relevant to stream sockets, but seems to affect UDP too on iOS) + // Disable SIGPIPE (should only be relevant to stream sockets, but seems to affect UDP too on iOS). int par = 1; - if (setsockopt(_sock, SOL_SOCKET, SO_NOSIGPIPE, SOCK_CBUF(&par), sizeof(int)) != 0) { - print_verbose("Unable to turn off SIGPIPE on socket"); + if (setsockopt(_sock, SOL_SOCKET, SO_NOSIGPIPE, &par, sizeof(int)) != 0) { + print_verbose("Unable to turn off SIGPIPE on socket."); } #endif return OK; } void NetSocketPosix::close() { - if (_sock != SOCK_EMPTY) { - SOCK_CLOSE(_sock); + if (_sock != -1) { + ::close(_sock); } - _sock = SOCK_EMPTY; + _sock = -1; _ip_type = IP::TYPE_NONE; _is_stream = false; } @@ -406,7 +318,7 @@ Error NetSocketPosix::bind(IPAddress p_addr, uint16_t p_port) { if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) { NetError err = _get_socket_error(); - print_verbose("Failed to bind socket. Error: " + itos(err)); + print_verbose("Failed to bind socket. Error: " + itos(err) + "."); close(); return ERR_UNAVAILABLE; } @@ -434,19 +346,19 @@ Error NetSocketPosix::connect_to_host(IPAddress p_host, uint16_t p_port) { struct sockaddr_storage addr; size_t addr_size = _set_addr_storage(&addr, p_host, p_port, _ip_type); - if (SOCK_CONNECT(_sock, (struct sockaddr *)&addr, addr_size) != 0) { + if (::connect(_sock, (struct sockaddr *)&addr, addr_size) != 0) { NetError err = _get_socket_error(); switch (err) { - // We are already connected + // We are already connected. case ERR_NET_IS_CONNECTED: return OK; - // Still waiting to connect, try again in a while + // Still waiting to connect, try again in a while. case ERR_NET_WOULD_BLOCK: case ERR_NET_IN_PROGRESS: return ERR_BUSY; default: - print_verbose("Connection to remote host failed!"); + print_verbose("Connection to remote host failed."); close(); return FAILED; } @@ -458,63 +370,6 @@ Error NetSocketPosix::connect_to_host(IPAddress p_host, uint16_t p_port) { Error NetSocketPosix::poll(PollType p_type, int p_timeout) const { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); -#if defined(WINDOWS_ENABLED) - bool ready = false; - fd_set rd, wr, ex; - fd_set *rdp = nullptr; - fd_set *wrp = nullptr; - FD_ZERO(&rd); - FD_ZERO(&wr); - FD_ZERO(&ex); - FD_SET(_sock, &ex); - struct timeval timeout = { p_timeout / 1000, (p_timeout % 1000) * 1000 }; - // For blocking operation, pass nullptr timeout pointer to select. - struct timeval *tp = nullptr; - if (p_timeout >= 0) { - // If timeout is non-negative, we want to specify the timeout instead. - tp = &timeout; - } - - switch (p_type) { - case POLL_TYPE_IN: - FD_SET(_sock, &rd); - rdp = &rd; - break; - case POLL_TYPE_OUT: - FD_SET(_sock, &wr); - wrp = ≀ - break; - case POLL_TYPE_IN_OUT: - FD_SET(_sock, &rd); - FD_SET(_sock, &wr); - rdp = &rd; - wrp = ≀ - } - int ret = select(1, rdp, wrp, &ex, tp); - - if (ret == SOCKET_ERROR) { - return FAILED; - } - - if (ret == 0) { - return ERR_BUSY; - } - - if (FD_ISSET(_sock, &ex)) { - _get_socket_error(); - print_verbose("Exception when polling socket."); - return FAILED; - } - - if (rdp && FD_ISSET(_sock, rdp)) { - ready = true; - } - if (wrp && FD_ISSET(_sock, wrp)) { - ready = true; - } - - return ready ? OK : ERR_BUSY; -#else struct pollfd pfd; pfd.fd = _sock; pfd.events = POLLIN; @@ -544,13 +399,12 @@ Error NetSocketPosix::poll(PollType p_type, int p_timeout) const { } return OK; -#endif } Error NetSocketPosix::recv(uint8_t *p_buffer, int p_len, int &r_read) { ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); - r_read = ::recv(_sock, SOCK_BUF(p_buffer), p_len, 0); + r_read = ::recv(_sock, p_buffer, p_len, 0); if (r_read < 0) { NetError err = _get_socket_error(); @@ -575,7 +429,7 @@ Error NetSocketPosix::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddr socklen_t len = sizeof(struct sockaddr_storage); memset(&from, 0, len); - r_read = ::recvfrom(_sock, SOCK_BUF(p_buffer), p_len, p_peek ? MSG_PEEK : 0, (struct sockaddr *)&from, &len); + r_read = ::recvfrom(_sock, p_buffer, p_len, p_peek ? MSG_PEEK : 0, (struct sockaddr *)&from, &len); if (r_read < 0) { NetError err = _get_socket_error(); @@ -615,7 +469,7 @@ Error NetSocketPosix::send(const uint8_t *p_buffer, int p_len, int &r_sent) { flags = MSG_NOSIGNAL; } #endif - r_sent = ::send(_sock, SOCK_CBUF(p_buffer), p_len, flags); + r_sent = ::send(_sock, p_buffer, p_len, flags); if (r_sent < 0) { NetError err = _get_socket_error(); @@ -637,7 +491,7 @@ Error NetSocketPosix::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IP struct sockaddr_storage addr; size_t addr_size = _set_addr_storage(&addr, p_ip, p_port, _ip_type); - r_sent = ::sendto(_sock, SOCK_CBUF(p_buffer), p_len, 0, (struct sockaddr *)&addr, addr_size); + r_sent = ::sendto(_sock, p_buffer, p_len, 0, (struct sockaddr *)&addr, addr_size); if (r_sent < 0) { NetError err = _get_socket_error(); @@ -662,8 +516,8 @@ Error NetSocketPosix::set_broadcasting_enabled(bool p_enabled) { } int par = p_enabled ? 1 : 0; - if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, SOCK_CBUF(&par), sizeof(int)) != 0) { - WARN_PRINT("Unable to change broadcast setting"); + if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, &par, sizeof(int)) != 0) { + WARN_PRINT("Unable to change broadcast setting."); return FAILED; } return OK; @@ -673,20 +527,15 @@ void NetSocketPosix::set_blocking_enabled(bool p_enabled) { ERR_FAIL_COND(!is_open()); int ret = 0; -#if defined(WINDOWS_ENABLED) - unsigned long par = p_enabled ? 0 : 1; - ret = SOCK_IOCTL(_sock, FIONBIO, &par); -#else int opts = fcntl(_sock, F_GETFL); if (p_enabled) { ret = fcntl(_sock, F_SETFL, opts & ~O_NONBLOCK); } else { ret = fcntl(_sock, F_SETFL, opts | O_NONBLOCK); } -#endif if (ret != 0) { - WARN_PRINT("Unable to change non-block mode"); + WARN_PRINT("Unable to change non-block mode."); } } @@ -696,56 +545,39 @@ void NetSocketPosix::set_ipv6_only_enabled(bool p_enabled) { ERR_FAIL_COND(_ip_type == IP::TYPE_IPV4); int par = p_enabled ? 1 : 0; - if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, SOCK_CBUF(&par), sizeof(int)) != 0) { - WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option"); + if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, &par, sizeof(int)) != 0) { + WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option."); } } void NetSocketPosix::set_tcp_no_delay_enabled(bool p_enabled) { ERR_FAIL_COND(!is_open()); - ERR_FAIL_COND(!_is_stream); // Not TCP + ERR_FAIL_COND(!_is_stream); // Not TCP. int par = p_enabled ? 1 : 0; - if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, SOCK_CBUF(&par), sizeof(int)) < 0) { - ERR_PRINT("Unable to set TCP no delay option"); + if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, &par, sizeof(int)) < 0) { + WARN_PRINT("Unable to set TCP no delay option."); } } void NetSocketPosix::set_reuse_address_enabled(bool p_enabled) { ERR_FAIL_COND(!is_open()); -// On Windows, enabling SO_REUSEADDR actually would also enable reuse port, very bad on TCP. Denying... -// Windows does not have this option, SO_REUSEADDR in this magical world means SO_REUSEPORT -#ifndef WINDOWS_ENABLED - int par = p_enabled ? 1 : 0; - if (setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, SOCK_CBUF(&par), sizeof(int)) < 0) { - WARN_PRINT("Unable to set socket REUSEADDR option!"); - } -#endif -} - -void NetSocketPosix::set_reuse_port_enabled(bool p_enabled) { - ERR_FAIL_COND(!is_open()); - -// See comment above... -#ifdef WINDOWS_ENABLED -#define SO_REUSEPORT SO_REUSEADDR -#endif int par = p_enabled ? 1 : 0; - if (setsockopt(_sock, SOL_SOCKET, SO_REUSEPORT, SOCK_CBUF(&par), sizeof(int)) < 0) { - WARN_PRINT("Unable to set socket REUSEPORT option!"); + if (setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR, &par, sizeof(int)) < 0) { + WARN_PRINT("Unable to set socket REUSEADDR option."); } } bool NetSocketPosix::is_open() const { - return _sock != SOCK_EMPTY; + return _sock != -1; } int NetSocketPosix::get_available_bytes() const { ERR_FAIL_COND_V(!is_open(), -1); - SOCK_FIONREAD_LEN_TYPE len; - int ret = SOCK_IOCTL(_sock, FIONREAD, &len); + int len; + int ret = ioctl(_sock, FIONREAD, &len); if (ret == -1) { _get_socket_error(); print_verbose("Error when checking available bytes on socket."); @@ -774,8 +606,8 @@ Ref<NetSocket> NetSocketPosix::accept(IPAddress &r_ip, uint16_t &r_port) { struct sockaddr_storage their_addr; socklen_t size = sizeof(their_addr); - SOCKET_TYPE fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size); - if (fd == SOCK_EMPTY) { + int fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size); + if (fd == -1) { _get_socket_error(); print_verbose("Error when accepting socket connection."); return out; @@ -797,4 +629,4 @@ Error NetSocketPosix::leave_multicast_group(const IPAddress &p_multi_address, co return _change_multicast_group(p_multi_address, p_if_name, false); } -#endif // UNIX_SOCKET_UNAVAILABLE +#endif // UNIX_ENABLED && !UNIX_SOCKET_UNAVAILABLE diff --git a/drivers/unix/net_socket_posix.h b/drivers/unix/net_socket_unix.h index aa59ff36ee..22f7bfdd91 100644 --- a/drivers/unix/net_socket_posix.h +++ b/drivers/unix/net_socket_unix.h @@ -1,5 +1,5 @@ /**************************************************************************/ -/* net_socket_posix.h */ +/* net_socket_unix.h */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,25 +28,18 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ -#ifndef NET_SOCKET_POSIX_H -#define NET_SOCKET_POSIX_H +#ifndef NET_SOCKET_UNIX_H +#define NET_SOCKET_UNIX_H -#include "core/io/net_socket.h" +#if defined(UNIX_ENABLED) && !defined(UNIX_SOCKET_UNAVAILABLE) -#if defined(WINDOWS_ENABLED) -#include <winsock2.h> -#include <ws2tcpip.h> -#define SOCKET_TYPE SOCKET +#include "core/io/net_socket.h" -#else #include <sys/socket.h> -#define SOCKET_TYPE int - -#endif class NetSocketPosix : public NetSocket { private: - SOCKET_TYPE _sock; // NOLINT - the default value is defined in the .cpp + int _sock = -1; IP::Type _ip_type = IP::TYPE_NONE; bool _is_stream = false; @@ -61,7 +54,7 @@ private: }; NetError _get_socket_error() const; - void _set_socket(SOCKET_TYPE p_sock, IP::Type p_ip_type, bool p_is_stream); + void _set_socket(int p_sock, IP::Type p_ip_type, bool p_is_stream); _FORCE_INLINE_ Error _change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add); _FORCE_INLINE_ void _set_close_exec_enabled(bool p_enabled); @@ -76,33 +69,34 @@ public: static void _set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port); static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type); - virtual Error open(Type p_sock_type, IP::Type &ip_type); - virtual void close(); - virtual Error bind(IPAddress p_addr, uint16_t p_port); - virtual Error listen(int p_max_pending); - virtual Error connect_to_host(IPAddress p_host, uint16_t p_port); - virtual Error poll(PollType p_type, int timeout) const; - virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read); - virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false); - virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent); - virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port); - virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port); - - virtual bool is_open() const; - virtual int get_available_bytes() const; - virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const; - - virtual Error set_broadcasting_enabled(bool p_enabled); - virtual void set_blocking_enabled(bool p_enabled); - virtual void set_ipv6_only_enabled(bool p_enabled); - virtual void set_tcp_no_delay_enabled(bool p_enabled); - virtual void set_reuse_address_enabled(bool p_enabled); - virtual void set_reuse_port_enabled(bool p_enabled); - virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name); - virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name); + virtual Error open(Type p_sock_type, IP::Type &ip_type) override; + virtual void close() override; + virtual Error bind(IPAddress p_addr, uint16_t p_port) override; + virtual Error listen(int p_max_pending) override; + virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override; + virtual Error poll(PollType p_type, int timeout) const override; + virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override; + virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override; + virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override; + virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override; + virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) override; + + virtual bool is_open() const override; + virtual int get_available_bytes() const override; + virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override; + + virtual Error set_broadcasting_enabled(bool p_enabled) override; + virtual void set_blocking_enabled(bool p_enabled) override; + virtual void set_ipv6_only_enabled(bool p_enabled) override; + virtual void set_tcp_no_delay_enabled(bool p_enabled) override; + virtual void set_reuse_address_enabled(bool p_enabled) override; + virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override; + virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override; NetSocketPosix(); - ~NetSocketPosix(); + ~NetSocketPosix() override; }; -#endif // NET_SOCKET_POSIX_H +#endif // UNIX_ENABLED && !UNIX_SOCKET_UNAVAILABLE + +#endif // NET_SOCKET_UNIX_H diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 8a9b130068..b23dba7a49 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -38,7 +38,7 @@ #include "drivers/unix/dir_access_unix.h" #include "drivers/unix/file_access_unix.h" #include "drivers/unix/file_access_unix_pipe.h" -#include "drivers/unix/net_socket_posix.h" +#include "drivers/unix/net_socket_unix.h" #include "drivers/unix/thread_posix.h" #include "servers/rendering_server.h" diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index f50771ddde..6eecd850f5 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -2611,7 +2611,10 @@ Error RenderingDeviceDriverVulkan::command_queue_execute_and_present(CommandQueu // it'll lead to very low performance in Android by entering an endless loop where it'll always resize the swap chain // every frame. - ERR_FAIL_COND_V(err != VK_SUCCESS && err != VK_SUBOPTIMAL_KHR, FAILED); + ERR_FAIL_COND_V_MSG( + err != VK_SUCCESS && err != VK_SUBOPTIMAL_KHR, + FAILED, + "QueuePresentKHR failed with error: " + get_vulkan_result(err)); } return OK; @@ -5434,6 +5437,23 @@ void RenderingDeviceDriverVulkan::print_lost_device_info() { on_device_lost(); } +inline String RenderingDeviceDriverVulkan::get_vulkan_result(VkResult err) { +#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED) + if (err == VK_ERROR_OUT_OF_HOST_MEMORY) { + return "VK_ERROR_OUT_OF_HOST_MEMORY"; + } else if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY) { + return "VK_ERROR_OUT_OF_DEVICE_MEMORY"; + } else if (err == VK_ERROR_DEVICE_LOST) { + return "VK_ERROR_DEVICE_LOST"; + } else if (err == VK_ERROR_SURFACE_LOST_KHR) { + return "VK_ERROR_SURFACE_LOST_KHR"; + } else if (err == VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT) { + return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT"; + } +#endif + return itos(err); +} + /********************/ /**** SUBMISSION ****/ /********************/ diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h index 6931015a22..06cd2a31be 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.h +++ b/drivers/vulkan/rendering_device_driver_vulkan.h @@ -655,6 +655,7 @@ public: virtual void command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) override final; void print_lost_device_info(); void on_device_lost() const; + static String get_vulkan_result(VkResult err); /********************/ /**** SUBMISSION ****/ diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp index 5c06295f14..7d2247d41a 100644 --- a/drivers/windows/file_access_windows.cpp +++ b/drivers/windows/file_access_windows.cpp @@ -127,7 +127,7 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) { } #ifdef TOOLS_ENABLED - // Windows is case insensitive, but all other platforms are sensitive to it + // Windows is case insensitive in the default configuration, but other platforms can be sensitive to it // To ease cross-platform development, we issue a warning if users try to access // a file using the wrong case (which *works* on Windows, but won't on other // platforms), we only check for relative paths, or paths in res:// or user://, diff --git a/drivers/windows/net_socket_winsock.cpp b/drivers/windows/net_socket_winsock.cpp new file mode 100644 index 0000000000..3fe7fc619e --- /dev/null +++ b/drivers/windows/net_socket_winsock.cpp @@ -0,0 +1,613 @@ +/**************************************************************************/ +/* net_socket_winsock.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifdef WINDOWS_ENABLED + +#include "net_socket_winsock.h" + +#include <winsock2.h> +#include <ws2tcpip.h> + +#include <mswsock.h> +// Workaround missing flag in MinGW +#if defined(__MINGW32__) && !defined(SIO_UDP_NETRESET) +#define SIO_UDP_NETRESET _WSAIOW(IOC_VENDOR, 15) +#endif + +size_t NetSocketWinSock::_set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type) { + memset(p_addr, 0, sizeof(struct sockaddr_storage)); + if (p_ip_type == IP::TYPE_IPV6 || p_ip_type == IP::TYPE_ANY) { // IPv6 socket. + + // IPv6 only socket with IPv4 address. + ERR_FAIL_COND_V(!p_ip.is_wildcard() && p_ip_type == IP::TYPE_IPV6 && p_ip.is_ipv4(), 0); + + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; + addr6->sin6_family = AF_INET6; + addr6->sin6_port = htons(p_port); + if (p_ip.is_valid()) { + memcpy(&addr6->sin6_addr.s6_addr, p_ip.get_ipv6(), 16); + } else { + addr6->sin6_addr = in6addr_any; + } + return sizeof(sockaddr_in6); + } else { // IPv4 socket. + + // IPv4 socket with IPv6 address. + ERR_FAIL_COND_V(!p_ip.is_wildcard() && !p_ip.is_ipv4(), 0); + + struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; + addr4->sin_family = AF_INET; + addr4->sin_port = htons(p_port); // Short, network byte order. + + if (p_ip.is_valid()) { + memcpy(&addr4->sin_addr.s_addr, p_ip.get_ipv4(), 4); + } else { + addr4->sin_addr.s_addr = INADDR_ANY; + } + + return sizeof(sockaddr_in); + } +} + +void NetSocketWinSock::_set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port) { + if (p_addr->ss_family == AF_INET) { + struct sockaddr_in *addr4 = (struct sockaddr_in *)p_addr; + if (r_ip) { + r_ip->set_ipv4((uint8_t *)&(addr4->sin_addr.s_addr)); + } + if (r_port) { + *r_port = ntohs(addr4->sin_port); + } + } else if (p_addr->ss_family == AF_INET6) { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)p_addr; + if (r_ip) { + r_ip->set_ipv6(addr6->sin6_addr.s6_addr); + } + if (r_port) { + *r_port = ntohs(addr6->sin6_port); + } + } +} + +NetSocket *NetSocketWinSock::_create_func() { + return memnew(NetSocketWinSock); +} + +void NetSocketWinSock::make_default() { + ERR_FAIL_COND(_create != nullptr); + + WSADATA data; + WSAStartup(MAKEWORD(2, 2), &data); + _create = _create_func; +} + +void NetSocketWinSock::cleanup() { + ERR_FAIL_COND(_create == nullptr); + + WSACleanup(); + _create = nullptr; +} + +NetSocketWinSock::NetSocketWinSock() { +} + +NetSocketWinSock::~NetSocketWinSock() { + close(); +} + +NetSocketWinSock::NetError NetSocketWinSock::_get_socket_error() const { + int err = WSAGetLastError(); + if (err == WSAEISCONN) { + return ERR_NET_IS_CONNECTED; + } + if (err == WSAEINPROGRESS || err == WSAEALREADY) { + return ERR_NET_IN_PROGRESS; + } + if (err == WSAEWOULDBLOCK) { + return ERR_NET_WOULD_BLOCK; + } + if (err == WSAEADDRINUSE || err == WSAEADDRNOTAVAIL) { + return ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE; + } + if (err == WSAEACCES) { + return ERR_NET_UNAUTHORIZED; + } + if (err == WSAEMSGSIZE || err == WSAENOBUFS) { + return ERR_NET_BUFFER_TOO_SMALL; + } + print_verbose("Socket error: " + itos(err) + "."); + return ERR_NET_OTHER; +} + +bool NetSocketWinSock::_can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const { + if (p_for_bind && !(p_ip.is_valid() || p_ip.is_wildcard())) { + return false; + } else if (!p_for_bind && !p_ip.is_valid()) { + return false; + } + // Check if socket support this IP type. + IP::Type type = p_ip.is_ipv4() ? IP::TYPE_IPV4 : IP::TYPE_IPV6; + return !(_ip_type != IP::TYPE_ANY && !p_ip.is_wildcard() && _ip_type != type); +} + +_FORCE_INLINE_ Error NetSocketWinSock::_change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!_can_use_ip(p_ip, false), ERR_INVALID_PARAMETER); + + // Need to force level and af_family to IP(v4) when using dual stacking and provided multicast group is IPv4. + IP::Type type = _ip_type == IP::TYPE_ANY && p_ip.is_ipv4() ? IP::TYPE_IPV4 : _ip_type; + // This needs to be the proper level for the multicast group, no matter if the socket is dual stacking. + int level = type == IP::TYPE_IPV4 ? IPPROTO_IP : IPPROTO_IPV6; + int ret = -1; + + IPAddress if_ip; + uint32_t if_v6id = 0; + HashMap<String, IP::Interface_Info> if_info; + IP::get_singleton()->get_local_interfaces(&if_info); + for (KeyValue<String, IP::Interface_Info> &E : if_info) { + IP::Interface_Info &c = E.value; + if (c.name != p_if_name) { + continue; + } + + if_v6id = (uint32_t)c.index.to_int(); + if (type == IP::TYPE_IPV6) { + break; // IPv6 uses index. + } + + for (const IPAddress &F : c.ip_addresses) { + if (!F.is_ipv4()) { + continue; // Wrong IP type. + } + if_ip = F; + break; + } + break; + } + + if (level == IPPROTO_IP) { + ERR_FAIL_COND_V(!if_ip.is_valid(), ERR_INVALID_PARAMETER); + struct ip_mreq greq; + int sock_opt = p_add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; + memcpy(&greq.imr_multiaddr, p_ip.get_ipv4(), 4); + memcpy(&greq.imr_interface, if_ip.get_ipv4(), 4); + ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq)); + } else { + struct ipv6_mreq greq; + int sock_opt = p_add ? IPV6_ADD_MEMBERSHIP : IPV6_DROP_MEMBERSHIP; + memcpy(&greq.ipv6mr_multiaddr, p_ip.get_ipv6(), 16); + greq.ipv6mr_interface = if_v6id; + ret = setsockopt(_sock, level, sock_opt, (const char *)&greq, sizeof(greq)); + } + ERR_FAIL_COND_V(ret != 0, FAILED); + + return OK; +} + +void NetSocketWinSock::_set_socket(SOCKET p_sock, IP::Type p_ip_type, bool p_is_stream) { + _sock = p_sock; + _ip_type = p_ip_type; + _is_stream = p_is_stream; +} + +Error NetSocketWinSock::open(Type p_sock_type, IP::Type &ip_type) { + ERR_FAIL_COND_V(is_open(), ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V(ip_type > IP::TYPE_ANY || ip_type < IP::TYPE_NONE, ERR_INVALID_PARAMETER); + + int family = ip_type == IP::TYPE_IPV4 ? AF_INET : AF_INET6; + int protocol = p_sock_type == TYPE_TCP ? IPPROTO_TCP : IPPROTO_UDP; + int type = p_sock_type == TYPE_TCP ? SOCK_STREAM : SOCK_DGRAM; + _sock = socket(family, type, protocol); + + if (_sock == INVALID_SOCKET && ip_type == IP::TYPE_ANY) { + // Careful here, changing the referenced parameter so the caller knows that we are using an IPv4 socket + // in place of a dual stack one, and further calls to _set_sock_addr will work as expected. + ip_type = IP::TYPE_IPV4; + family = AF_INET; + _sock = socket(family, type, protocol); + } + + ERR_FAIL_COND_V(_sock == INVALID_SOCKET, FAILED); + _ip_type = ip_type; + + if (family == AF_INET6) { + // Select IPv4 over IPv6 mapping. + set_ipv6_only_enabled(ip_type != IP::TYPE_ANY); + } + + if (protocol == IPPROTO_UDP) { + // Make sure to disable broadcasting for UDP sockets. + // Depending on the OS, this option might or might not be enabled by default. Let's normalize it. + set_broadcasting_enabled(false); + } + + _is_stream = p_sock_type == TYPE_TCP; + + if (!_is_stream) { + // Disable windows feature/bug reporting WSAECONNRESET/WSAENETRESET when + // recv/recvfrom and an ICMP reply was received from a previous send/sendto. + unsigned long disable = 0; + if (ioctlsocket(_sock, SIO_UDP_CONNRESET, &disable) == SOCKET_ERROR) { + print_verbose("Unable to turn off UDP WSAECONNRESET behavior on Windows."); + } + if (ioctlsocket(_sock, SIO_UDP_NETRESET, &disable) == SOCKET_ERROR) { + // This feature seems not to be supported on wine. + print_verbose("Unable to turn off UDP WSAENETRESET behavior on Windows."); + } + } + return OK; +} + +void NetSocketWinSock::close() { + if (_sock != INVALID_SOCKET) { + closesocket(_sock); + } + + _sock = INVALID_SOCKET; + _ip_type = IP::TYPE_NONE; + _is_stream = false; +} + +Error NetSocketWinSock::bind(IPAddress p_addr, uint16_t p_port) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!_can_use_ip(p_addr, true), ERR_INVALID_PARAMETER); + + sockaddr_storage addr; + size_t addr_size = _set_addr_storage(&addr, p_addr, p_port, _ip_type); + + if (::bind(_sock, (struct sockaddr *)&addr, addr_size) != 0) { + NetError err = _get_socket_error(); + print_verbose("Failed to bind socket. Error: " + itos(err) + "."); + close(); + return ERR_UNAVAILABLE; + } + + return OK; +} + +Error NetSocketWinSock::listen(int p_max_pending) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + if (::listen(_sock, p_max_pending) != 0) { + _get_socket_error(); + print_verbose("Failed to listen from socket."); + close(); + return FAILED; + } + + return OK; +} + +Error NetSocketWinSock::connect_to_host(IPAddress p_host, uint16_t p_port) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + ERR_FAIL_COND_V(!_can_use_ip(p_host, false), ERR_INVALID_PARAMETER); + + struct sockaddr_storage addr; + size_t addr_size = _set_addr_storage(&addr, p_host, p_port, _ip_type); + + if (::WSAConnect(_sock, (struct sockaddr *)&addr, addr_size, nullptr, nullptr, nullptr, nullptr) != 0) { + NetError err = _get_socket_error(); + + switch (err) { + // We are already connected. + case ERR_NET_IS_CONNECTED: + return OK; + // Still waiting to connect, try again in a while. + case ERR_NET_WOULD_BLOCK: + case ERR_NET_IN_PROGRESS: + return ERR_BUSY; + default: + print_verbose("Connection to remote host failed."); + close(); + return FAILED; + } + } + + return OK; +} + +Error NetSocketWinSock::poll(PollType p_type, int p_timeout) const { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + bool ready = false; + fd_set rd, wr, ex; + fd_set *rdp = nullptr; + fd_set *wrp = nullptr; + FD_ZERO(&rd); + FD_ZERO(&wr); + FD_ZERO(&ex); + FD_SET(_sock, &ex); + struct timeval timeout = { p_timeout / 1000, (p_timeout % 1000) * 1000 }; + // For blocking operation, pass nullptr timeout pointer to select. + struct timeval *tp = nullptr; + if (p_timeout >= 0) { + // If timeout is non-negative, we want to specify the timeout instead. + tp = &timeout; + } + + switch (p_type) { + case POLL_TYPE_IN: + FD_SET(_sock, &rd); + rdp = &rd; + break; + case POLL_TYPE_OUT: + FD_SET(_sock, &wr); + wrp = ≀ + break; + case POLL_TYPE_IN_OUT: + FD_SET(_sock, &rd); + FD_SET(_sock, &wr); + rdp = &rd; + wrp = ≀ + } + // WSAPoll is broken: https://daniel.haxx.se/blog/2012/10/10/wsapoll-is-broken/. + int ret = select(1, rdp, wrp, &ex, tp); + + if (ret == SOCKET_ERROR) { + return FAILED; + } + + if (ret == 0) { + return ERR_BUSY; + } + + if (FD_ISSET(_sock, &ex)) { + _get_socket_error(); + print_verbose("Exception when polling socket."); + return FAILED; + } + + if (rdp && FD_ISSET(_sock, rdp)) { + ready = true; + } + if (wrp && FD_ISSET(_sock, wrp)) { + ready = true; + } + + return ready ? OK : ERR_BUSY; +} + +Error NetSocketWinSock::recv(uint8_t *p_buffer, int p_len, int &r_read) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + r_read = ::recv(_sock, (char *)p_buffer, p_len, 0); + + if (r_read < 0) { + NetError err = _get_socket_error(); + if (err == ERR_NET_WOULD_BLOCK) { + return ERR_BUSY; + } + + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } + + return FAILED; + } + + return OK; +} + +Error NetSocketWinSock::recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + struct sockaddr_storage from; + socklen_t len = sizeof(struct sockaddr_storage); + memset(&from, 0, len); + + r_read = ::recvfrom(_sock, (char *)p_buffer, p_len, p_peek ? MSG_PEEK : 0, (struct sockaddr *)&from, &len); + + if (r_read < 0) { + NetError err = _get_socket_error(); + if (err == ERR_NET_WOULD_BLOCK) { + return ERR_BUSY; + } + + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } + + return FAILED; + } + + if (from.ss_family == AF_INET) { + struct sockaddr_in *sin_from = (struct sockaddr_in *)&from; + r_ip.set_ipv4((uint8_t *)&sin_from->sin_addr); + r_port = ntohs(sin_from->sin_port); + } else if (from.ss_family == AF_INET6) { + struct sockaddr_in6 *s6_from = (struct sockaddr_in6 *)&from; + r_ip.set_ipv6((uint8_t *)&s6_from->sin6_addr); + r_port = ntohs(s6_from->sin6_port); + } else { + // Unsupported socket family, should never happen. + ERR_FAIL_V(FAILED); + } + + return OK; +} + +Error NetSocketWinSock::send(const uint8_t *p_buffer, int p_len, int &r_sent) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + int flags = 0; + r_sent = ::send(_sock, (const char *)p_buffer, p_len, flags); + + if (r_sent < 0) { + NetError err = _get_socket_error(); + if (err == ERR_NET_WOULD_BLOCK) { + return ERR_BUSY; + } + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } + + return FAILED; + } + + return OK; +} + +Error NetSocketWinSock::sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + + struct sockaddr_storage addr; + size_t addr_size = _set_addr_storage(&addr, p_ip, p_port, _ip_type); + r_sent = ::sendto(_sock, (const char *)p_buffer, p_len, 0, (struct sockaddr *)&addr, addr_size); + + if (r_sent < 0) { + NetError err = _get_socket_error(); + if (err == ERR_NET_WOULD_BLOCK) { + return ERR_BUSY; + } + if (err == ERR_NET_BUFFER_TOO_SMALL) { + return ERR_OUT_OF_MEMORY; + } + + return FAILED; + } + + return OK; +} + +Error NetSocketWinSock::set_broadcasting_enabled(bool p_enabled) { + ERR_FAIL_COND_V(!is_open(), ERR_UNCONFIGURED); + // IPv6 has no broadcast support. + if (_ip_type == IP::TYPE_IPV6) { + return ERR_UNAVAILABLE; + } + + int par = p_enabled ? 1 : 0; + if (setsockopt(_sock, SOL_SOCKET, SO_BROADCAST, (const char *)&par, sizeof(int)) != 0) { + WARN_PRINT("Unable to change broadcast setting."); + return FAILED; + } + return OK; +} + +void NetSocketWinSock::set_blocking_enabled(bool p_enabled) { + ERR_FAIL_COND(!is_open()); + + int ret = 0; + unsigned long par = p_enabled ? 0 : 1; + ret = ioctlsocket(_sock, FIONBIO, &par); + if (ret != 0) { + WARN_PRINT("Unable to change non-block mode."); + } +} + +void NetSocketWinSock::set_ipv6_only_enabled(bool p_enabled) { + ERR_FAIL_COND(!is_open()); + // This option is only available in IPv6 sockets. + ERR_FAIL_COND(_ip_type == IP::TYPE_IPV4); + + int par = p_enabled ? 1 : 0; + if (setsockopt(_sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char *)&par, sizeof(int)) != 0) { + WARN_PRINT("Unable to change IPv4 address mapping over IPv6 option."); + } +} + +void NetSocketWinSock::set_tcp_no_delay_enabled(bool p_enabled) { + ERR_FAIL_COND(!is_open()); + ERR_FAIL_COND(!_is_stream); // Not TCP. + + int par = p_enabled ? 1 : 0; + if (setsockopt(_sock, IPPROTO_TCP, TCP_NODELAY, (const char *)&par, sizeof(int)) < 0) { + WARN_PRINT("Unable to set TCP no delay option."); + } +} + +void NetSocketWinSock::set_reuse_address_enabled(bool p_enabled) { + ERR_FAIL_COND(!is_open()); + + // On Windows, enabling SO_REUSEADDR actually would also enable reuse port, very bad on TCP. Denying... + // Windows does not have this option, SO_REUSEADDR in this magical world means SO_REUSEPORT +} + +bool NetSocketWinSock::is_open() const { + return _sock != INVALID_SOCKET; +} + +int NetSocketWinSock::get_available_bytes() const { + ERR_FAIL_COND_V(!is_open(), -1); + + unsigned long len; + int ret = ioctlsocket(_sock, FIONREAD, &len); + if (ret == -1) { + _get_socket_error(); + print_verbose("Error when checking available bytes on socket."); + return -1; + } + return len; +} + +Error NetSocketWinSock::get_socket_address(IPAddress *r_ip, uint16_t *r_port) const { + ERR_FAIL_COND_V(!is_open(), FAILED); + + struct sockaddr_storage saddr; + socklen_t len = sizeof(saddr); + if (getsockname(_sock, (struct sockaddr *)&saddr, &len) != 0) { + _get_socket_error(); + print_verbose("Error when reading local socket address."); + return FAILED; + } + _set_ip_port(&saddr, r_ip, r_port); + return OK; +} + +Ref<NetSocket> NetSocketWinSock::accept(IPAddress &r_ip, uint16_t &r_port) { + Ref<NetSocket> out; + ERR_FAIL_COND_V(!is_open(), out); + + struct sockaddr_storage their_addr; + socklen_t size = sizeof(their_addr); + SOCKET fd = ::accept(_sock, (struct sockaddr *)&their_addr, &size); + if (fd == INVALID_SOCKET) { + _get_socket_error(); + print_verbose("Error when accepting socket connection."); + return out; + } + + _set_ip_port(&their_addr, &r_ip, &r_port); + + NetSocketWinSock *ns = memnew(NetSocketWinSock); + ns->_set_socket(fd, _ip_type, _is_stream); + ns->set_blocking_enabled(false); + return Ref<NetSocket>(ns); +} + +Error NetSocketWinSock::join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) { + return _change_multicast_group(p_multi_address, p_if_name, true); +} + +Error NetSocketWinSock::leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) { + return _change_multicast_group(p_multi_address, p_if_name, false); +} + +#endif // WINDOWS_ENABLED diff --git a/drivers/windows/net_socket_winsock.h b/drivers/windows/net_socket_winsock.h new file mode 100644 index 0000000000..5c3445b8cb --- /dev/null +++ b/drivers/windows/net_socket_winsock.h @@ -0,0 +1,102 @@ +/**************************************************************************/ +/* net_socket_winsock.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef NET_SOCKET_WINSOCK_H +#define NET_SOCKET_WINSOCK_H + +#ifdef WINDOWS_ENABLED + +#include "core/io/net_socket.h" + +#include <winsock2.h> +#include <ws2tcpip.h> + +class NetSocketWinSock : public NetSocket { +private: + SOCKET _sock = INVALID_SOCKET; + IP::Type _ip_type = IP::TYPE_NONE; + bool _is_stream = false; + + enum NetError { + ERR_NET_WOULD_BLOCK, + ERR_NET_IS_CONNECTED, + ERR_NET_IN_PROGRESS, + ERR_NET_ADDRESS_INVALID_OR_UNAVAILABLE, + ERR_NET_UNAUTHORIZED, + ERR_NET_BUFFER_TOO_SMALL, + ERR_NET_OTHER, + }; + + NetError _get_socket_error() const; + void _set_socket(SOCKET p_sock, IP::Type p_ip_type, bool p_is_stream); + _FORCE_INLINE_ Error _change_multicast_group(IPAddress p_ip, String p_if_name, bool p_add); + +protected: + static NetSocket *_create_func(); + + bool _can_use_ip(const IPAddress &p_ip, const bool p_for_bind) const; + +public: + static void make_default(); + static void cleanup(); + static void _set_ip_port(struct sockaddr_storage *p_addr, IPAddress *r_ip, uint16_t *r_port); + static size_t _set_addr_storage(struct sockaddr_storage *p_addr, const IPAddress &p_ip, uint16_t p_port, IP::Type p_ip_type); + + virtual Error open(Type p_sock_type, IP::Type &ip_type) override; + virtual void close() override; + virtual Error bind(IPAddress p_addr, uint16_t p_port) override; + virtual Error listen(int p_max_pending) override; + virtual Error connect_to_host(IPAddress p_host, uint16_t p_port) override; + virtual Error poll(PollType p_type, int timeout) const override; + virtual Error recv(uint8_t *p_buffer, int p_len, int &r_read) override; + virtual Error recvfrom(uint8_t *p_buffer, int p_len, int &r_read, IPAddress &r_ip, uint16_t &r_port, bool p_peek = false) override; + virtual Error send(const uint8_t *p_buffer, int p_len, int &r_sent) override; + virtual Error sendto(const uint8_t *p_buffer, int p_len, int &r_sent, IPAddress p_ip, uint16_t p_port) override; + virtual Ref<NetSocket> accept(IPAddress &r_ip, uint16_t &r_port) override; + + virtual bool is_open() const override; + virtual int get_available_bytes() const override; + virtual Error get_socket_address(IPAddress *r_ip, uint16_t *r_port) const override; + + virtual Error set_broadcasting_enabled(bool p_enabled) override; + virtual void set_blocking_enabled(bool p_enabled) override; + virtual void set_ipv6_only_enabled(bool p_enabled) override; + virtual void set_tcp_no_delay_enabled(bool p_enabled) override; + virtual void set_reuse_address_enabled(bool p_enabled) override; + virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override; + virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) override; + + NetSocketWinSock(); + ~NetSocketWinSock() override; +}; + +#endif // WINDOWS_ENABLED + +#endif // NET_SOCKET_WINSOCK_H |
