Skip to content

Commit

Permalink
Architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
Lecrapouille committed Dec 10, 2024
1 parent 5d73626 commit 26b5d1e
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 85 deletions.
3 changes: 3 additions & 0 deletions addons/gdcef/demos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ You can run demos directly if you have already compiled gdCEF (`cd .. && /build.

Just open your Godot editor 4.2+ and search for the `project.godot` file of the desired demo. Open the demo and run it directly inside the Godot editor.

## Understanding the architecture

Please refer to the [architecture](../doc/architecture.md) documentation for more details.
### Demo 00: Hello CEF

A "Hello-world" demo showing a 2D GUI split vertically into two browsers. Only the left browser accepts mouse and keyboard events. The right browser does not allow interaction since a timer is used for loading different URLs one by one. No keyboard events and mouse events are sent to the browser.
Expand Down
133 changes: 85 additions & 48 deletions addons/gdcef/doc/architecture.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Software Architecture

This document details the software architecture of [gdcef](https://github.com/Lecrapouille/gdcef), a native module for the [Godot engine](https://godotengine.org/) that implements the [Chromium Embedded Framework](https://bitbucket.org/chromiumembedded/cef/wiki/Home) (CEF).
This document details the software architecture of [gdCEF](https://github.com/Lecrapouille/gdcef), a GDExtension for the [Godot engine](https://godotengine.org/) that implements [Chromium Embedded Framework](https://bitbucket.org/chromiumembedded/cef/wiki/Home) webviews.

## Overview

The gdcef module consists of two main components:
The gdCEF consists of two main processes:

1. A main process that handles CEF initialization and browser management
2. A render process that handles web page rendering and JavaScript execution
Expand All @@ -15,64 +15,102 @@ The gdcef module consists of two main components:

Two main classes are exposed to Godot as Nodes:

#### GDCef Class

Implemented in `gdcef/src/gdcef.hpp`, this class serves as the entry point:

- **GDCef**: The entry point class that:
- Initializes CEF and manages its lifecycle
- Handles CEF settings and configuration
- Creates and manages browser instances
- Routes messages between CEF subprocesses

- **GDBrowserView**: Represents a browser instance that:
- Manages web page display and rendering
- Handles user interactions (mouse, keyboard)
- Controls page navigation and JavaScript execution
- Manages audio streaming
- Handles file downloads
- Initializes CEF and manages its lifecycle.
- Handles CEF settings and configuration.
- Creates and manages browser instances.
- Routes messages between CEF subprocesses.
- Shall be considered as a singleton.

- **GDBrowserView**: Represents a browser view instance that:
- Manages web page display and rendering.
- Handles user interactions (mouse, keyboard).
- Controls page navigation and JavaScript execution.
- Manages audio streaming.
- Handles file downloads.

These classes are derived from `godot::Node`, allowing them to be integrated into Godot's scene tree:

![CEF node integration](pics/cef.png)

`GDBrowserView` shall not directly be created by the user, but by the `GDCef` class through the `create_browser_view()` method. They are automatically added to the scene tree when created as a child of a `GDCef` node. They are automatically removed from the scene tree when destroyed or when closed. They are not shown in the Godot scene tree by design by Godot.

In the above picture, the `Control` node is a 2D Control node used for the layout of the browser view. A `TextureRect` node is used to display the web page.

```gdscript
var settings = {...}
var browser = $CEF.create_browser("https://www.google.com", $TextureRect, settings)
```

### Render Process

The render process is implemented in a separate executable and handles:

- Web page rendering
- JavaScript execution
- V8 context management
- Communication with the main process
- Web page rendering.
- JavaScript execution.
- V8 context management.
- Communication with the main process.

It is mandatory to for offscreen rendering (avoiding native window creation). The drawback is the loose of GPU compositing.

## Communication Flow

### Initialization Sequence

![Init Sequence](architecture/sequence_init.png)

1. GDScript initializes GDCef with configuration
2. CEF initializes and forks required processes
3. The render process starts and initializes its components
4. Browser instances can then be created
1. GDScript initializes GDCef with configuration.
2. CEF initializes and forks required processes.
3. The render process starts and initializes its components.
4. Browser instances can then be created.

### Rendering Sequence

![Paint Sequence](architecture/sequence_paint.png)

1. The render process renders web content
2. Content is painted to an off-screen buffer
3. The buffer is converted to a Godot texture
4. The texture is displayed in the Godot scene
1. The render process renders web content.
2. Content is painted to an off-screen buffer.
3. The buffer is converted to a Godot texture.
4. The texture is displayed in the Godot scene.

### Download Sequence

![Download Sequence](architecture/sequence_download.png)

1. Download is initiated from browser
2. CEF handles the download process
3. Progress updates are sent to Godot
4. Download completion is signaled
1. Download is initiated from browser.
2. CEF handles the download process.
3. Progress updates are sent to Godot.
4. Download completion is signaled.

### JavaScript/Godot Communication

The module implements bidirectional communication between JavaScript and Godot through an IPC (Inter-Process Communication) system:

![JS Communication Sequence](architecture/sequence_js_communication.png)

#### Godot to JavaScript

When Godot needs to send data to JavaScript:
1. GDScript calls `send_to_js(event_name, data)`.
2. The Main Process sends an IPC message to the Render Process.
3. The Render Process executes JavaScript code that emits an event.
4. JavaScript callbacks registered with `godotEventSystem.on()` receive the data.

#### JavaScript to Godot

When JavaScript needs to call Godot methods:
1. JavaScript calls methods through the `window.godot` proxy.
2. The Render Process converts JavaScript arguments to CEF types.
3. An IPC message is sent to the Main Process.
4. The Main Process executes the corresponding GDScript callable.

### Event System

The module injects a JavaScript event system into web pages:
- `window.godotEventSystem.on(event_name, callback)`: Register event listeners.
- `window.godot.methodName()`: Call Godot methods directly.

Example usage:

## Class Relationships

Expand All @@ -82,25 +120,24 @@ The following diagram shows the relationships between the main components:

### Key Relationships

- GDCef creates and manages GDBrowserView instances
- GDBrowserView communicates with CEF browser instances
- The render process communicates with both GDCef and browser instances
- All CEF-related classes implement appropriate CEF interfaces
- GDCef creates and manages GDBrowserView instances.
- GDBrowserView communicates with CEF browser instances.
- The render process communicates with both GDCef and browser instances.
- All CEF-related classes implement appropriate CEF interfaces.

## Implementation Details

- The module uses CEF's windowless rendering mode for seamless integration with Godot
- Audio is routed through Godot's audio system
- JavaScript integration allows bidirectional communication between Godot and web content
- File downloads are managed through CEF's download handler interface
- Mouse and keyboard events are translated from Godot to CEF format
- The module uses CEF's windowless rendering mode for seamless integration with Godot.
- Audio is routed through Godot's audio system.
- JavaScript integration allows bidirectional communication between Godot and web content.
- File downloads are managed through CEF's download handler interface.
- Mouse and keyboard events are translated from Godot to CEF format.

## Technical Constraints

- CEF requires a separate render process executable
- The render process path must be canonical
- CEF modifies command line arguments during initialization
- Memory management must account for both Godot's reference counting and CEF's reference counting

For API documentation and usage examples, please refer to [API.md](API.md).
- CEF requires a separate render process executable.
- The render process path must be canonical.
- CEF modifies command line arguments during initialization.
- Memory management must account for both Godot's reference counting and CEF's reference counting.

For API documentation and usage examples, please refer to [API.md](API.md).
Binary file modified addons/gdcef/doc/architecture/sequence_js_communication.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 31 additions & 37 deletions addons/gdcef/doc/architecture/sequence_js_communication.puml
Original file line number Diff line number Diff line change
@@ -1,60 +1,54 @@
@startuml

participant "JavaScript" as JS
participant "Render Process" as RP
participant "CEF" as CEF
participant "GDCef\n(Main Process)" as MP
participant "GDScript" as GD
participant "Main Process\n(Godot)" as MP
participant "Render Process\n(CEF)" as RP
participant "JavaScript\n(Renderer Process)" as JS

== JavaScript to GDScript ==
== Godot to JavaScript ==

JS -> RP : godot.callGodotMethod()
GD -> MP : send_to_js("character_update", data)
activate MP

MP -> RP : SendProcessMessage("GodotToJS", ["character_update", data])
activate RP

RP -> RP : GodotMethodHandler::Execute()
RP -> CEF : SendProcessMessage(PID_BROWSER)
activate CEF
RP -> RP : OnProcessMessageReceived()
RP -> JS : ExecuteJavaScript("godotEventSystem.emit()")
activate JS

CEF -> MP : OnProcessMessageReceived()
activate MP
JS -> JS : Execute registered callback
note right: Registered with godotEventSystem.on()
JS --> JS : Update UI
deactivate JS

MP -> GD : Call GDScript method
activate GD
GD --> MP : Return value
deactivate GD
RP --> MP : Message processed
deactivate RP

MP --> CEF : Process message result
MP --> GD : Complete
deactivate MP

CEF --> RP : Message processed
deactivate CEF
== JavaScript to Godot ==

RP --> JS : JavaScript return value
deactivate RP
JS -> RP : window.godot.modify_xp(42)
activate RP

== GDScript to JavaScript ==
RP -> RP : GodotMethodHandler::Execute()
note right: Convert JS args to CEF types

GD -> MP : execute_javascript()
RP -> MP : SendProcessMessage("callGodotMethod", ["modify_xp", 42])
activate MP

MP -> CEF : ExecuteJavaScript()
activate CEF
MP -> MP : OnProcessMessageReceived()
MP -> GD : Execute Callable("modify_xp")
activate GD

CEF -> RP : Execute in V8 context
activate RP
GD -> GD : Update game state
deactivate GD

RP -> JS : Execute JavaScript code
activate JS
JS --> RP : JavaScript result
deactivate JS
MP --> RP : Complete
deactivate MP

RP --> CEF : Execution complete
deactivate RP

CEF --> MP : JavaScript executed
deactivate CEF

MP --> GD : Execution complete
deactivate MP

@enduml
Binary file modified addons/gdcef/doc/pics/cef.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 26b5d1e

Please sign in to comment.