Skip to content

Commit

Permalink
Implement ability to render viewports directly to screen
Browse files Browse the repository at this point in the history
  • Loading branch information
clayjohn authored and myhalibobo committed Sep 3, 2019
1 parent 85c4b79 commit 41bcc8f
Show file tree
Hide file tree
Showing 18 changed files with 178 additions and 4 deletions.
3 changes: 3 additions & 0 deletions doc/classes/Viewport.xml
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@
<member name="physics_object_picking" type="bool" setter="set_physics_object_picking" getter="get_physics_object_picking">
If [code]true[/code], the objects rendered by viewport become subjects of mouse picking process. Default value: [code]false[/code].
</member>
<member name="render_direct_to_screen" type="bool" setter="set_use_render_direct_to_screen" getter="is_using_render_direct_to_screen">
If [code]true[/code], renders the Viewport directly to the screen instead of to the root viewport. Only available in GLES2. This is a low-level optimization and should not be used in most cases. If used, reading from the Viewport or from [code]SCREEN_TEXTURE[/code] becomes unavailable. For more information see [method VisualServer.viewport_set_render_direct_to_screen]. Default value: [code]false[/code].
</member>
<member name="render_target_clear_mode" type="int" setter="set_clear_mode" getter="get_clear_mode" enum="Viewport.ClearMode">
The clear mode when viewport used as a render target. Default value: [code]CLEAR_MODE_ALWAYS[/code].
</member>
Expand Down
20 changes: 19 additions & 1 deletion doc/classes/VisualServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3590,7 +3590,14 @@
<argument index="2" name="screen" type="int" default="0">
</argument>
<description>
Attaches a viewport to a screen.
Copies viewport to a region of the screen specified by [code]rect[/code]. If Viewport.[member Viewport.render_direct_to_screen] is [code]true[/code], then viewport does not use a framebuffer and the contents of the viewport are rendered directly to screen. However, note that the root viewport is drawn last, therefore it will draw over the screen. Accordingly, you must set the root viewport to an area that does not cover the area that you have attached this viewport to.
For example, you can set the root viewport to not render at all with the following code:
[codeblock]
func _ready():
get_viewport().set_attach_to_screen_rect(Rect2())
$Viewport.set_attach_to_screen_rect(Rect2(0, 0, 600, 600))
[/codeblock]
Using this can result in significant optimization, especially on lower-end devices. However, it comes at the cost of having to manage your viewports manually. For a further optimization see, [method set_render_direct_to_screen].
</description>
</method>
<method name="viewport_create">
Expand Down Expand Up @@ -3789,6 +3796,17 @@
Sets the viewport's parent to another viewport.
</description>
</method>
<method name="viewport_set_render_direct_to_screen">
<return type="void">
</return>
<argument index="0" name="viewport" type="RID">
</argument>
<argument index="1" name="enabled" type="bool">
</argument>
<description>
If [code]true[/code], render the contents of the viewport directly to screen. This allows a low-level optimization where you can skip drawing a viewport to the root viewport. While this optimization can result in a significant increase in speed (especially on older devices), it comes at a cost of usability. When this is enabled, you cannot read from the viewport or from the [code]SCREEN_TEXTURE[/code]. You also lose the benefit of certain window settings, such as the various stretch modes. Another consequence to be aware of is that in 2D the rendering happens in window coordinates, so if you have a viewport that is double the size of the window, and you set this, then only the portion that fits within the window will be drawn, no automatic scaling is possible, even if your game scene is significantly larger than the window size.
</description>
</method>
<method name="viewport_set_scenario">
<return type="void">
</return>
Expand Down
1 change: 1 addition & 0 deletions drivers/dummy/rasterizer_dummy.h
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,7 @@ class RasterizerStorageDummy : public RasterizerStorage {
/* RENDER TARGET */

RID render_target_create() { return RID(); }
void render_target_set_position(RID p_render_target, int p_x, int p_y) {}
void render_target_set_size(RID p_render_target, int p_width, int p_height) {}
RID render_target_get_texture(RID p_render_target) const { return RID(); }
void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) {}
Expand Down
26 changes: 26 additions & 0 deletions drivers/gles2/rasterizer_canvas_gles2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,22 @@ void RasterizerCanvasGLES2::canvas_begin() {

state.canvas_shader.bind();
state.using_transparent_rt = false;
int viewport_x, viewport_y, viewport_width, viewport_height;

if (storage->frame.current_rt) {
glBindFramebuffer(GL_FRAMEBUFFER, storage->frame.current_rt->fbo);
state.using_transparent_rt = storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_TRANSPARENT];

if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_DIRECT_TO_SCREEN]) {
// set Viewport and Scissor when rendering directly to screen
viewport_width = storage->frame.current_rt->width;
viewport_height = storage->frame.current_rt->height;
viewport_x = storage->frame.current_rt->x;
viewport_y = OS::get_singleton()->get_window_size().height - viewport_height - storage->frame.current_rt->y;
glScissor(viewport_x, viewport_y, viewport_width, viewport_height);
glViewport(viewport_x, viewport_y, viewport_width, viewport_height);
glEnable(GL_SCISSOR_TEST);
}
}

if (storage->frame.clear_request) {
Expand Down Expand Up @@ -179,6 +192,14 @@ void RasterizerCanvasGLES2::canvas_end() {
glDisableVertexAttribArray(i);
}

if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_DIRECT_TO_SCREEN]) {
//reset viewport to full window size
int viewport_width = OS::get_singleton()->get_window_size().width;
int viewport_height = OS::get_singleton()->get_window_size().height;
glViewport(0, 0, viewport_width, viewport_height);
glScissor(0, 0, viewport_width, viewport_height);
}

state.using_texture_rect = false;
state.using_skeleton = false;
state.using_ninepatch = false;
Expand Down Expand Up @@ -1192,6 +1213,11 @@ void RasterizerCanvasGLES2::_canvas_item_render_commands(Item *p_item, Item *cur

void RasterizerCanvasGLES2::_copy_screen(const Rect2 &p_rect) {

if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_DIRECT_TO_SCREEN]) {
ERR_PRINT_ONCE("Cannot use screen texture copying in render target set to render direct to screen");
return;
}

if (storage->frame.current_rt->copy_screen_effect.color == 0) {
ERR_EXPLAIN("Can't use screen texture copying in a render target configured without copy buffers");
ERR_FAIL();
Expand Down
20 changes: 19 additions & 1 deletion drivers/gles2/rasterizer_scene_gles2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2695,6 +2695,7 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
Environment *env = NULL;

int viewport_width, viewport_height;
int viewport_x, viewport_y;
bool probe_interior = false;
bool reverse_cull = false;

Expand Down Expand Up @@ -2734,6 +2735,13 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const

viewport_width = storage->frame.current_rt->width;
viewport_height = storage->frame.current_rt->height;
viewport_x = storage->frame.current_rt->x;

if (storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_DIRECT_TO_SCREEN]) {
viewport_y = OS::get_singleton()->get_window_size().height - viewport_height - storage->frame.current_rt->y;
} else {
viewport_y = storage->frame.current_rt->y;
}
}

state.used_screen_texture = false;
Expand Down Expand Up @@ -2799,7 +2807,13 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const
// other stuff

glBindFramebuffer(GL_FRAMEBUFFER, current_fb);
glViewport(0, 0, viewport_width, viewport_height);
glViewport(viewport_x, viewport_y, viewport_width, viewport_height);

if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_DIRECT_TO_SCREEN]) {

glScissor(viewport_x, viewport_y, viewport_width, viewport_height);
glEnable(GL_SCISSOR_TEST);
}

glDepthFunc(GL_LEQUAL);
glDepthMask(GL_TRUE);
Expand Down Expand Up @@ -2834,6 +2848,10 @@ void RasterizerSceneGLES2::render_scene(const Transform &p_cam_transform, const

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

if (storage->frame.current_rt && storage->frame.current_rt->flags[RasterizerStorage::RENDER_TARGET_DIRECT_TO_SCREEN]) {
glDisable(GL_SCISSOR_TEST);
}

glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1);

glBlendEquation(GL_FUNC_ADD);
Expand Down
28 changes: 28 additions & 0 deletions drivers/gles2/rasterizer_storage_gles2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4568,9 +4568,16 @@ void RasterizerStorageGLES2::instance_remove_dependency(RID p_base, RasterizerSc

void RasterizerStorageGLES2::_render_target_allocate(RenderTarget *rt) {

// do not allocate a render target with no size
if (rt->width <= 0 || rt->height <= 0)
return;

// do not allocate a render target that is attached to the screen
if (rt->flags[RENDER_TARGET_DIRECT_TO_SCREEN]) {
rt->fbo = RasterizerStorageGLES2::system_fbo;
return;
}

GLuint color_internal_format;
GLuint color_format;
GLuint color_type = GL_UNSIGNED_BYTE;
Expand Down Expand Up @@ -4779,6 +4786,10 @@ void RasterizerStorageGLES2::_render_target_allocate(RenderTarget *rt) {

void RasterizerStorageGLES2::_render_target_clear(RenderTarget *rt) {

// there is nothing to clear when DIRECT_TO_SCREEN is used
if (rt->flags[RENDER_TARGET_DIRECT_TO_SCREEN])
return;

if (rt->fbo) {
glDeleteFramebuffers(1, &rt->fbo);
glDeleteTextures(1, &rt->color);
Expand Down Expand Up @@ -4873,6 +4884,15 @@ RID RasterizerStorageGLES2::render_target_create() {
return render_target_owner.make_rid(rt);
}

void RasterizerStorageGLES2::render_target_set_position(RID p_render_target, int p_x, int p_y) {

RenderTarget *rt = render_target_owner.getornull(p_render_target);
ERR_FAIL_COND(!rt);

rt->x = p_x;
rt->y = p_y;
}

void RasterizerStorageGLES2::render_target_set_size(RID p_render_target, int p_width, int p_height) {

RenderTarget *rt = render_target_owner.getornull(p_render_target);
Expand Down Expand Up @@ -5002,6 +5022,14 @@ void RasterizerStorageGLES2::render_target_set_flag(RID p_render_target, RenderT
RenderTarget *rt = render_target_owner.getornull(p_render_target);
ERR_FAIL_COND(!rt);

// When setting DIRECT_TO_SCREEN, you need to clear before the value is set, but allocate after as
// those functions change how they operate depending on the value of DIRECT_TO_SCREEN
if (p_flag == RENDER_TARGET_DIRECT_TO_SCREEN && p_value != rt->flags[RENDER_TARGET_DIRECT_TO_SCREEN]) {
_render_target_clear(rt);
rt->flags[p_flag] = p_value;
_render_target_allocate(rt);
}

rt->flags[p_flag] = p_value;

switch (p_flag) {
Expand Down
5 changes: 4 additions & 1 deletion drivers/gles2/rasterizer_storage_gles2.h
Original file line number Diff line number Diff line change
Expand Up @@ -1175,7 +1175,7 @@ class RasterizerStorageGLES2 : public RasterizerStorage {
}
} external;

int width, height;
int x, y, width, height;

bool flags[RENDER_TARGET_FLAG_MAX];

Expand All @@ -1192,6 +1192,8 @@ class RasterizerStorageGLES2 : public RasterizerStorage {
multisample_color(0),
multisample_depth(0),
multisample_active(false),
x(0),
y(0),
width(0),
height(0),
used_in_frame(false),
Expand All @@ -1209,6 +1211,7 @@ class RasterizerStorageGLES2 : public RasterizerStorage {
void _render_target_allocate(RenderTarget *rt);

virtual RID render_target_create();
virtual void render_target_set_position(RID p_render_target, int p_x, int p_y);
virtual void render_target_set_size(RID p_render_target, int p_width, int p_height);
virtual RID render_target_get_texture(RID p_render_target) const;
virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id);
Expand Down
4 changes: 4 additions & 0 deletions drivers/gles3/rasterizer_storage_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7270,6 +7270,10 @@ RID RasterizerStorageGLES3::render_target_create() {
return render_target_owner.make_rid(rt);
}

void RasterizerStorageGLES3::render_target_set_position(RID p_render_target, int p_x, int p_y) {
//only used in GLES2
}

void RasterizerStorageGLES3::render_target_set_size(RID p_render_target, int p_width, int p_height) {

RenderTarget *rt = render_target_owner.getornull(p_render_target);
Expand Down
1 change: 1 addition & 0 deletions drivers/gles3/rasterizer_storage_gles3.h
Original file line number Diff line number Diff line change
Expand Up @@ -1394,6 +1394,7 @@ class RasterizerStorageGLES3 : public RasterizerStorage {
void _render_target_allocate(RenderTarget *rt);

virtual RID render_target_create();
virtual void render_target_set_position(RID p_render_target, int p_x, int p_y);
virtual void render_target_set_size(RID p_render_target, int p_width, int p_height);
virtual RID render_target_get_texture(RID p_render_target) const;
virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id);
Expand Down
18 changes: 18 additions & 0 deletions scene/main/viewport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2739,6 +2739,19 @@ Rect2 Viewport::get_attach_to_screen_rect() const {
return to_screen_rect;
}

void Viewport::set_use_render_direct_to_screen(bool p_render_direct_to_screen) {

if (p_render_direct_to_screen == render_direct_to_screen)
return;

render_direct_to_screen = p_render_direct_to_screen;
VS::get_singleton()->viewport_set_render_direct_to_screen(viewport, p_render_direct_to_screen);
}

bool Viewport::is_using_render_direct_to_screen() const {
return render_direct_to_screen;
}

void Viewport::set_physics_object_picking(bool p_enable) {

physics_object_picking = p_enable;
Expand Down Expand Up @@ -3004,6 +3017,8 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_as_audio_listener_2d", "enable"), &Viewport::set_as_audio_listener_2d);
ClassDB::bind_method(D_METHOD("is_audio_listener_2d"), &Viewport::is_audio_listener_2d);
ClassDB::bind_method(D_METHOD("set_attach_to_screen_rect", "rect"), &Viewport::set_attach_to_screen_rect);
ClassDB::bind_method(D_METHOD("set_use_render_direct_to_screen", "enable"), &Viewport::set_use_render_direct_to_screen);
ClassDB::bind_method(D_METHOD("is_using_render_direct_to_screen"), &Viewport::is_using_render_direct_to_screen);

ClassDB::bind_method(D_METHOD("get_mouse_position"), &Viewport::get_mouse_position);
ClassDB::bind_method(D_METHOD("warp_mouse", "to_position"), &Viewport::warp_mouse);
Expand Down Expand Up @@ -3058,6 +3073,7 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_3d_linear"), "set_keep_3d_linear", "get_keep_3d_linear");
ADD_PROPERTY(PropertyInfo(Variant::INT, "usage", PROPERTY_HINT_ENUM, "2D,2D No-Sampling,3D,3D No-Effects"), "set_usage", "get_usage");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "render_direct_to_screen"), "set_use_render_direct_to_screen", "is_using_render_direct_to_screen");
ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw");
ADD_GROUP("Render Target", "render_target_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "render_target_v_flip"), "set_vflip", "get_vflip");
Expand Down Expand Up @@ -3140,6 +3156,8 @@ Viewport::Viewport() {
texture_rid = VisualServer::get_singleton()->viewport_get_texture(viewport);
texture_flags = 0;

render_direct_to_screen = false;

default_texture.instance();
default_texture->vp = const_cast<Viewport *>(this);
viewport_textures.insert(default_texture.ptr());
Expand Down
4 changes: 4 additions & 0 deletions scene/main/viewport.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ class Viewport : public Node {

Size2 size;
Rect2 to_screen_rect;
bool render_direct_to_screen;

RID contact_2d_debug;
RID contact_3d_debug_multimesh;
Expand Down Expand Up @@ -481,6 +482,9 @@ class Viewport : public Node {
void set_attach_to_screen_rect(const Rect2 &p_rect);
Rect2 get_attach_to_screen_rect() const;

void set_use_render_direct_to_screen(bool p_render_direct_to_screen);
bool is_using_render_direct_to_screen() const;

Vector2 get_mouse_position() const;
void warp_mouse(const Vector2 &p_pos);

Expand Down
2 changes: 2 additions & 0 deletions servers/visual/rasterizer.h
Original file line number Diff line number Diff line change
Expand Up @@ -550,10 +550,12 @@ class RasterizerStorage {
RENDER_TARGET_NO_SAMPLING,
RENDER_TARGET_HDR,
RENDER_TARGET_KEEP_3D_LINEAR,
RENDER_TARGET_DIRECT_TO_SCREEN,
RENDER_TARGET_FLAG_MAX
};

virtual RID render_target_create() = 0;
virtual void render_target_set_position(RID p_render_target, int p_x, int p_y) = 0;
virtual void render_target_set_size(RID p_render_target, int p_width, int p_height) = 0;
virtual RID render_target_get_texture(RID p_render_target) const = 0;
virtual void render_target_set_external_texture(RID p_render_target, unsigned int p_texture_id) = 0;
Expand Down
1 change: 1 addition & 0 deletions servers/visual/visual_server_raster.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ class VisualServerRaster : public VisualServer {
BIND2(viewport_set_clear_mode, RID, ViewportClearMode)

BIND3(viewport_attach_to_screen, RID, const Rect2 &, int)
BIND2(viewport_set_render_direct_to_screen, RID, bool)
BIND1(viewport_detach, RID)

BIND2(viewport_set_update_mode, RID, ViewportUpdateMode)
Expand Down
Loading

0 comments on commit 41bcc8f

Please sign in to comment.