Merge pull request #42119 from lawnjelly/ewok3

Unified GLES3 / GLES2 Batching
This commit is contained in:
Rémi Verschelde 2020-10-19 15:01:10 +02:00 committed by GitHub
commit 123942f61f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 7361 additions and 4495 deletions

View file

@ -1062,6 +1062,10 @@
<member name="rendering/limits/time/time_rollover_secs" type="float" setter="" getter="" default="3600"> <member name="rendering/limits/time/time_rollover_secs" type="float" setter="" getter="" default="3600">
Shaders have a time variable that constantly increases. At some point, it needs to be rolled back to zero to avoid precision errors on shader animations. This setting specifies when (in seconds). Shaders have a time variable that constantly increases. At some point, it needs to be rolled back to zero to avoid precision errors on shader animations. This setting specifies when (in seconds).
</member> </member>
<member name="rendering/quality/2d/ninepatch_mode" type="int" setter="" getter="" default="0">
Choose between default mode where corner scalings are preserved matching the artwork, and scaling mode.
Not available in GLES3 when [member rendering/batching/options/use_batching] is off.
</member>
<member name="rendering/quality/2d/use_nvidia_rect_flicker_workaround" type="bool" setter="" getter="" default="false"> <member name="rendering/quality/2d/use_nvidia_rect_flicker_workaround" type="bool" setter="" getter="" default="false">
Some NVIDIA GPU drivers have a bug which produces flickering issues for the [code]draw_rect[/code] method, especially as used in [TileMap]. Refer to [url=https://github.com/godotengine/godot/issues/9913]GitHub issue 9913[/url] for details. Some NVIDIA GPU drivers have a bug which produces flickering issues for the [code]draw_rect[/code] method, especially as used in [TileMap]. Refer to [url=https://github.com/godotengine/godot/issues/9913]GitHub issue 9913[/url] for details.
If [code]true[/code], this option enables a "safe" code path for such NVIDIA GPUs at the cost of performance. This option affects GLES2 and GLES3 rendering, but only on desktop platforms. If [code]true[/code], this option enables a "safe" code path for such NVIDIA GPUs at the cost of performance. This option affects GLES2 and GLES3 rendering, but only on desktop platforms.
@ -1070,6 +1074,10 @@
If [code]true[/code], forces snapping of polygons to pixels in 2D rendering. May help in some pixel art styles. If [code]true[/code], forces snapping of polygons to pixels in 2D rendering. May help in some pixel art styles.
Consider using the project setting [member rendering/batching/precision/uv_contract] to prevent artifacts. Consider using the project setting [member rendering/batching/precision/uv_contract] to prevent artifacts.
</member> </member>
<member name="rendering/quality/2d/use_software_skinning" type="bool" setter="" getter="" default="true">
If [code]true[/code], performs 2d skinning on the CPU rather than the GPU. This provides greater compatibility with a wide range of hardware, and also may be faster in some circumstances.
Currently only available when [member rendering/batching/options/use_batching] is active.
</member>
<member name="rendering/quality/depth/hdr" type="bool" setter="" getter="" default="true"> <member name="rendering/quality/depth/hdr" type="bool" setter="" getter="" default="true">
If [code]true[/code], allocates the main framebuffer with high dynamic range. High dynamic range allows the use of [Color] values greater than 1. If [code]true[/code], allocates the main framebuffer with high dynamic range. High dynamic range allows the use of [Color] values greater than 1.
[b]Note:[/b] Only available on the GLES3 backend. [b]Note:[/b] Only available on the GLES3 backend.

View file

@ -26,6 +26,7 @@ SConscript("winmidi/SCsub")
if env["platform"] != "server": if env["platform"] != "server":
SConscript("gles3/SCsub") SConscript("gles3/SCsub")
SConscript("gles2/SCsub") SConscript("gles2/SCsub")
SConscript("gles_common/SCsub")
SConscript("gl_context/SCsub") SConscript("gl_context/SCsub")
else: else:
SConscript("dummy/SCsub") SConscript("dummy/SCsub")

View file

@ -56,7 +56,12 @@ void RasterizerCanvasBaseGLES2::canvas_begin() {
// always start with light_angle unset // always start with light_angle unset
state.using_light_angle = false; state.using_light_angle = false;
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_LIGHT_ANGLE, false); state.using_large_vertex = false;
state.using_modulate = false;
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LIGHT_ANGLE, false);
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_MODULATE, false);
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LARGE_VERTEX, false);
state.canvas_shader.bind(); state.canvas_shader.bind();
int viewport_x, viewport_y, viewport_width, viewport_height; int viewport_x, viewport_y, viewport_width, viewport_height;
@ -160,13 +165,23 @@ void RasterizerCanvasBaseGLES2::draw_generic_textured_rect(const Rect2 &p_rect,
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
} }
void RasterizerCanvasBaseGLES2::_set_texture_rect_mode(bool p_texture_rect, bool p_light_angle) { void RasterizerCanvasBaseGLES2::_set_texture_rect_mode(bool p_texture_rect, bool p_light_angle, bool p_modulate, bool p_large_vertex) {
// always set this directly (this could be state checked) // always set this directly (this could be state checked)
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, p_texture_rect); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_TEXTURE_RECT, p_texture_rect);
if (state.using_light_angle != p_light_angle) { if (state.using_light_angle != p_light_angle) {
state.using_light_angle = p_light_angle; state.using_light_angle = p_light_angle;
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_LIGHT_ANGLE, p_light_angle); state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LIGHT_ANGLE, p_light_angle);
}
if (state.using_modulate != p_modulate) {
state.using_modulate = p_modulate;
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_MODULATE, p_modulate);
}
if (state.using_large_vertex != p_large_vertex) {
state.using_large_vertex = p_large_vertex;
state.canvas_shader.set_conditional(CanvasShaderGLES2::USE_ATTRIB_LARGE_VERTEX, p_large_vertex);
} }
} }
@ -431,8 +446,21 @@ void RasterizerCanvasBaseGLES2::_draw_polygon(const int *p_indices, int p_index_
glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer);
// precalculate the buffer size ahead of time instead of making multiple glBufferSubData calls
uint32_t total_buffer_size = sizeof(Vector2) * p_vertex_count;
if (!p_singlecolor && p_colors) {
total_buffer_size += sizeof(Color) * p_vertex_count;
}
if (p_uvs) {
total_buffer_size += sizeof(Vector2) * p_vertex_count;
}
if (p_weights && p_bones) {
total_buffer_size += sizeof(float) * 4 * p_vertex_count; // weights
total_buffer_size += sizeof(int) * 4 * p_vertex_count; // bones
}
uint32_t buffer_ofs = 0; uint32_t buffer_ofs = 0;
storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, sizeof(Vector2) * p_vertex_count, p_vertices); storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, total_buffer_size, p_vertices);
glEnableVertexAttribArray(VS::ARRAY_VERTEX); glEnableVertexAttribArray(VS::ARRAY_VERTEX);
glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), NULL); glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), NULL);
@ -446,14 +474,12 @@ void RasterizerCanvasBaseGLES2::_draw_polygon(const int *p_indices, int p_index_
glDisableVertexAttribArray(VS::ARRAY_COLOR); glDisableVertexAttribArray(VS::ARRAY_COLOR);
glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1);
} else { } else {
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors);
glEnableVertexAttribArray(VS::ARRAY_COLOR); glEnableVertexAttribArray(VS::ARRAY_COLOR);
glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs));
buffer_ofs += sizeof(Color) * p_vertex_count; buffer_ofs += sizeof(Color) * p_vertex_count;
} }
if (p_uvs) { if (p_uvs) {
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs);
glEnableVertexAttribArray(VS::ARRAY_TEX_UV); glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs));
buffer_ofs += sizeof(Vector2) * p_vertex_count; buffer_ofs += sizeof(Vector2) * p_vertex_count;
@ -462,12 +488,10 @@ void RasterizerCanvasBaseGLES2::_draw_polygon(const int *p_indices, int p_index_
} }
if (p_weights && p_bones) { if (p_weights && p_bones) {
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(float) * 4 * p_vertex_count, p_weights);
glEnableVertexAttribArray(VS::ARRAY_WEIGHTS); glEnableVertexAttribArray(VS::ARRAY_WEIGHTS);
glVertexAttribPointer(VS::ARRAY_WEIGHTS, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(buffer_ofs)); glVertexAttribPointer(VS::ARRAY_WEIGHTS, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 4, CAST_INT_TO_UCHAR_PTR(buffer_ofs));
buffer_ofs += sizeof(float) * 4 * p_vertex_count; buffer_ofs += sizeof(float) * 4 * p_vertex_count;
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(int) * 4 * p_vertex_count, p_bones);
glEnableVertexAttribArray(VS::ARRAY_BONES); glEnableVertexAttribArray(VS::ARRAY_BONES);
glVertexAttribPointer(VS::ARRAY_BONES, 4, GL_UNSIGNED_INT, GL_FALSE, sizeof(int) * 4, CAST_INT_TO_UCHAR_PTR(buffer_ofs)); glVertexAttribPointer(VS::ARRAY_BONES, 4, GL_UNSIGNED_INT, GL_FALSE, sizeof(int) * 4, CAST_INT_TO_UCHAR_PTR(buffer_ofs));
buffer_ofs += sizeof(int) * 4 * p_vertex_count; buffer_ofs += sizeof(int) * 4 * p_vertex_count;
@ -501,8 +525,17 @@ void RasterizerCanvasBaseGLES2::_draw_generic(GLuint p_primitive, int p_vertex_c
glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer); glBindBuffer(GL_ARRAY_BUFFER, data.polygon_buffer);
// precalculate the buffer size ahead of time instead of making multiple glBufferSubData calls
uint32_t total_buffer_size = sizeof(Vector2) * p_vertex_count;
if (!p_singlecolor && p_colors) {
total_buffer_size += sizeof(Color) * p_vertex_count;
}
if (p_uvs) {
total_buffer_size += sizeof(Vector2) * p_vertex_count;
}
uint32_t buffer_ofs = 0; uint32_t buffer_ofs = 0;
storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, sizeof(Vector2) * p_vertex_count, p_vertices); storage->buffer_orphan_and_upload(data.polygon_buffer_size, 0, total_buffer_size, p_vertices);
glEnableVertexAttribArray(VS::ARRAY_VERTEX); glEnableVertexAttribArray(VS::ARRAY_VERTEX);
glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), NULL); glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), NULL);
@ -516,14 +549,12 @@ void RasterizerCanvasBaseGLES2::_draw_generic(GLuint p_primitive, int p_vertex_c
glDisableVertexAttribArray(VS::ARRAY_COLOR); glDisableVertexAttribArray(VS::ARRAY_COLOR);
glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1); glVertexAttrib4f(VS::ARRAY_COLOR, 1, 1, 1, 1);
} else { } else {
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Color) * p_vertex_count, p_colors);
glEnableVertexAttribArray(VS::ARRAY_COLOR); glEnableVertexAttribArray(VS::ARRAY_COLOR);
glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); glVertexAttribPointer(VS::ARRAY_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(Color), CAST_INT_TO_UCHAR_PTR(buffer_ofs));
buffer_ofs += sizeof(Color) * p_vertex_count; buffer_ofs += sizeof(Color) * p_vertex_count;
} }
if (p_uvs) { if (p_uvs) {
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs);
glEnableVertexAttribArray(VS::ARRAY_TEX_UV); glEnableVertexAttribArray(VS::ARRAY_TEX_UV);
glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs)); glVertexAttribPointer(VS::ARRAY_TEX_UV, 2, GL_FLOAT, GL_FALSE, sizeof(Vector2), CAST_INT_TO_UCHAR_PTR(buffer_ofs));
} else { } else {
@ -1024,10 +1055,4 @@ void RasterizerCanvasBaseGLES2::finalize() {
} }
RasterizerCanvasBaseGLES2::RasterizerCanvasBaseGLES2() { RasterizerCanvasBaseGLES2::RasterizerCanvasBaseGLES2() {
#ifdef GLES_OVER_GL
use_nvidia_rect_workaround = GLOBAL_GET("rendering/quality/2d/use_nvidia_rect_flicker_workaround");
#else
// Not needed (a priori) on GLES devices
use_nvidia_rect_workaround = false;
#endif
} }

View file

@ -31,13 +31,14 @@
#ifndef RASTERIZERCANVASBASEGLES2_H #ifndef RASTERIZERCANVASBASEGLES2_H
#define RASTERIZERCANVASBASEGLES2_H #define RASTERIZERCANVASBASEGLES2_H
#include "rasterizer_array_gles2.h" #include "drivers/gles_common/rasterizer_array.h"
#include "rasterizer_storage_gles2.h" #include "rasterizer_storage_gles2.h"
#include "servers/visual/rasterizer.h" #include "servers/visual/rasterizer.h"
#include "shaders/canvas.glsl.gen.h" #include "shaders/canvas.glsl.gen.h"
#include "shaders/lens_distorted.glsl.gen.h" #include "shaders/lens_distorted.glsl.gen.h"
#include "drivers/gles_common/rasterizer_storage_common.h"
#include "shaders/canvas_shadow.glsl.gen.h" #include "shaders/canvas_shadow.glsl.gen.h"
class RasterizerCanvasBaseGLES2 : public RasterizerCanvas { class RasterizerCanvasBaseGLES2 : public RasterizerCanvas {
@ -77,7 +78,11 @@ public:
LensDistortedShaderGLES2 lens_shader; LensDistortedShaderGLES2 lens_shader;
bool using_texture_rect; bool using_texture_rect;
bool using_light_angle; bool using_light_angle;
bool using_modulate;
bool using_large_vertex;
bool using_ninepatch; bool using_ninepatch;
bool using_skeleton; bool using_skeleton;
@ -102,8 +107,6 @@ public:
RasterizerStorageGLES2 *storage; RasterizerStorageGLES2 *storage;
bool use_nvidia_rect_workaround;
void _set_uniforms(); void _set_uniforms();
virtual RID light_internal_create(); virtual RID light_internal_create();
@ -131,7 +134,7 @@ public:
virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow); virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow);
RasterizerStorageGLES2::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map); RasterizerStorageGLES2::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map);
void _set_texture_rect_mode(bool p_texture_rect, bool p_light_angle = false); void _set_texture_rect_mode(bool p_texture_rect, bool p_light_angle = false, bool p_modulate = false, bool p_large_vertex = false);
void initialize(); void initialize();
void finalize(); void finalize();

File diff suppressed because it is too large Load diff

View file

@ -31,297 +31,14 @@
#ifndef RASTERIZERCANVASGLES2_H #ifndef RASTERIZERCANVASGLES2_H
#define RASTERIZERCANVASGLES2_H #define RASTERIZERCANVASGLES2_H
#include "drivers/gles_common/rasterizer_canvas_batcher.h"
#include "rasterizer_canvas_base_gles2.h" #include "rasterizer_canvas_base_gles2.h"
class RasterizerSceneGLES2; class RasterizerSceneGLES2;
class RasterizerCanvasGLES2 : public RasterizerCanvasBaseGLES2 { class RasterizerCanvasGLES2 : public RasterizerCanvasBaseGLES2, public RasterizerCanvasBatcher<RasterizerCanvasGLES2, RasterizerStorageGLES2> {
// used to determine whether we use hardware transform (none) friend class RasterizerCanvasBatcher<RasterizerCanvasGLES2, RasterizerStorageGLES2>;
// software transform all verts, or software transform just a translate
// (no rotate or scale)
enum TransformMode {
TM_NONE,
TM_ALL,
TM_TRANSLATE,
};
// pod versions of vector and color and RID, need to be 32 bit for vertex format
struct BatchVector2 {
float x, y;
void set(const Vector2 &p_o) {
x = p_o.x;
y = p_o.y;
}
void to(Vector2 &r_o) const {
r_o.x = x;
r_o.y = y;
}
};
struct BatchColor {
float r, g, b, a;
void set(const Color &p_c) {
r = p_c.r;
g = p_c.g;
b = p_c.b;
a = p_c.a;
}
bool operator==(const BatchColor &p_c) const {
return (r == p_c.r) && (g == p_c.g) && (b == p_c.b) && (a == p_c.a);
}
bool operator!=(const BatchColor &p_c) const { return (*this == p_c) == false; }
bool equals(const Color &p_c) const {
return (r == p_c.r) && (g == p_c.g) && (b == p_c.b) && (a == p_c.a);
}
const float *get_data() const { return &r; }
String to_string() const;
};
struct BatchVertex {
// must be 32 bit pod
BatchVector2 pos;
BatchVector2 uv;
};
struct BatchVertexColored : public BatchVertex {
// must be 32 bit pod
BatchColor col;
};
struct BatchVertexLightAngled : public BatchVertexColored {
// must be pod
float light_angle;
};
struct Batch {
enum CommandType : uint32_t {
BT_DEFAULT,
BT_RECT,
};
CommandType type;
uint32_t first_command; // also item reference number
uint32_t num_commands;
uint32_t first_quad;
uint32_t batch_texture_id;
BatchColor color;
};
struct BatchTex {
enum TileMode : uint32_t {
TILE_OFF,
TILE_NORMAL,
TILE_FORCE_REPEAT,
};
RID RID_texture;
RID RID_normal;
TileMode tile_mode;
BatchVector2 tex_pixel_size;
uint32_t flags;
};
// items in a list to be sorted prior to joining
struct BSortItem {
// have a function to keep as pod, rather than operator
void assign(const BSortItem &o) {
item = o.item;
z_index = o.z_index;
}
Item *item;
int z_index;
};
// batch item may represent 1 or more items
struct BItemJoined {
uint32_t first_item_ref;
uint32_t num_item_refs;
Rect2 bounding_rect;
// note the z_index may only be correct for the first of the joined item references
// this has implications for light culling with z ranged lights.
int16_t z_index;
// these are defined in RasterizerStorageGLES2::Shader::CanvasItem::BatchFlags
uint16_t flags;
// we are always splitting items with lots of commands,
// and items with unhandled primitives (default)
bool use_hardware_transform() const { return num_item_refs == 1; }
};
struct BItemRef {
Item *item;
Color final_modulate;
};
struct BLightRegion {
void reset() {
light_bitfield = 0;
shadow_bitfield = 0;
too_many_lights = false;
}
uint64_t light_bitfield;
uint64_t shadow_bitfield;
bool too_many_lights; // we can only do light region optimization if there are 64 or less lights
};
struct BatchData {
BatchData();
void reset_flush() {
batches.reset();
batch_textures.reset();
vertices.reset();
light_angles.reset();
total_quads = 0;
total_color_changes = 0;
use_light_angles = false;
}
GLuint gl_vertex_buffer;
GLuint gl_index_buffer;
uint32_t max_quads;
uint32_t vertex_buffer_size_units;
uint32_t vertex_buffer_size_bytes;
uint32_t index_buffer_size_units;
uint32_t index_buffer_size_bytes;
// small vertex FVF type - pos and UV.
// This will always be written to initially, but can be translated
// to larger FVFs if necessary.
RasterizerArrayGLES2<BatchVertex> vertices;
// extra data which can be stored during prefilling, for later translation to larger FVFs
RasterizerArrayGLES2<float> light_angles;
// instead of having a different buffer for each vertex FVF type
// we have a special array big enough for the biggest FVF
// which can have a changeable unit size, and reuse it.
RasterizerUnitArrayGLES2 unit_vertices;
RasterizerArrayGLES2<Batch> batches;
RasterizerArrayGLES2<Batch> batches_temp; // used for translating to colored vertex batches
RasterizerArray_non_pod_GLES2<BatchTex> batch_textures; // the only reason this is non-POD is because of RIDs
// flexible vertex format.
// all verts have pos and UV.
// some have color, some light angles etc.
bool use_colored_vertices;
bool use_light_angles;
RasterizerArrayGLES2<BItemJoined> items_joined;
RasterizerArrayGLES2<BItemRef> item_refs;
// items are sorted prior to joining
RasterizerArrayGLES2<BSortItem> sort_items;
// counts
int total_quads;
// we keep a record of how many color changes caused new batches
// if the colors are causing an excessive number of batches, we switch
// to alternate batching method and add color to the vertex format.
int total_color_changes;
// if the shader is using MODULATE, we prevent baking color so the final_modulate can
// be read in the shader.
// if the shader is reading VERTEX, we prevent baking vertex positions with extra matrices etc
// to prevent the read position being incorrect.
// These flags are defined in RasterizerStorageGLES2::Shader::CanvasItem::BatchFlags
uint32_t joined_item_batch_flags;
// measured in pixels, recalculated each frame
float scissor_threshold_area;
// diagnose this frame, every nTh frame when settings_diagnose_frame is on
bool diagnose_frame;
String frame_string;
uint32_t next_diagnose_tick;
uint64_t diagnose_frame_number;
// whether to join items across z_indices - this can interfere with z ranged lights,
// so has to be disabled in some circumstances
bool join_across_z_indices;
// global settings
bool settings_use_batching; // the current use_batching (affected by flash)
bool settings_use_batching_original_choice; // the choice entered in project settings
bool settings_flash_batching; // for regression testing, flash between non-batched and batched renderer
bool settings_diagnose_frame; // print out batches to help optimize / regression test
int settings_max_join_item_commands;
float settings_colored_vertex_format_threshold;
int settings_batch_buffer_num_verts;
bool settings_scissor_lights;
float settings_scissor_threshold; // 0.0 to 1.0
int settings_item_reordering_lookahead;
bool settings_use_single_rect_fallback;
int settings_light_max_join_items;
// uv contraction
bool settings_uv_contract;
float settings_uv_contract_amount;
// only done on diagnose frame
void reset_stats() {
stats_items_sorted = 0;
stats_light_items_joined = 0;
}
// frame stats (just for monitoring and debugging)
int stats_items_sorted;
int stats_light_items_joined;
} bdata;
struct RenderItemState {
RenderItemState() { reset(); }
void reset();
Item *current_clip;
RasterizerStorageGLES2::Shader *shader_cache;
bool rebind_shader;
bool prev_use_skeleton;
int last_blend_mode;
RID canvas_last_material;
Color final_modulate;
// used for joining items only
BItemJoined *joined_item;
bool join_batch_break;
BLightRegion light_region;
// 'item group' is data over a single call to canvas_render_items
int item_group_z;
Color item_group_modulate;
Light *item_group_light;
Transform2D item_group_base_transform;
} _render_item_state;
struct FillState {
void reset() {
// don't reset members that need to be preserved after flushing
// half way through a list of commands
curr_batch = 0;
batch_tex_id = -1;
texpixel_size = Vector2(1, 1);
contract_uvs = false;
}
Batch *curr_batch;
int batch_tex_id;
bool use_hardware_transform;
bool contract_uvs;
Vector2 texpixel_size;
Color final_modulate;
TransformMode transform_mode;
TransformMode orig_transform_mode;
// support for extra matrices
bool extra_matrix_sent; // whether sent on this item (in which case software transform can't be used untl end of item)
int transform_extra_command_number_p1; // plus one to allow fast checking against zero
Transform2D transform_combined; // final * extra
};
public: public:
virtual void canvas_render_items_begin(const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform); virtual void canvas_render_items_begin(const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
@ -332,598 +49,27 @@ public:
private: private:
// legacy codepath .. to remove after testing // legacy codepath .. to remove after testing
void _canvas_render_item(Item *p_ci, RenderItemState &r_ris); void _legacy_canvas_render_item(Item *p_ci, RenderItemState &r_ris);
void _canvas_item_render_commands(Item *p_item, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material);
// high level batch funcs // high level batch funcs
void canvas_render_items_implementation(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform); void canvas_render_items_implementation(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
void render_joined_item(const BItemJoined &p_bij, RenderItemState &r_ris); void render_joined_item(const BItemJoined &p_bij, RenderItemState &r_ris);
void record_items(Item *p_item_list, int p_z);
void join_items(Item *p_item_list, int p_z);
void join_sorted_items();
bool try_join_item(Item *p_ci, RenderItemState &r_ris, bool &r_batch_break); bool try_join_item(Item *p_ci, RenderItemState &r_ris, bool &r_batch_break);
void render_joined_item_commands(const BItemJoined &p_bij, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material, bool p_lit);
void render_batches(Item::Command *const *p_commands, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material); void render_batches(Item::Command *const *p_commands, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material);
bool prefill_joined_item(FillState &r_fill_state, int &r_command_start, Item *p_item, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material);
void flush_render_batches(Item *p_first_item, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES2::Material *p_material);
// low level batch funcs // low level batch funcs
int _batch_find_or_create_tex(const RID &p_texture, const RID &p_normal, bool p_tile, int p_previous_match);
RasterizerStorageGLES2::Texture *_get_canvas_texture(const RID &p_texture) const;
void _batch_upload_buffers(); void _batch_upload_buffers();
void _batch_render_rects(const Batch &p_batch, RasterizerStorageGLES2::Material *p_material); void _batch_render_rects(const Batch &p_batch, RasterizerStorageGLES2::Material *p_material);
BatchVertex *_batch_vertex_request_new() { return bdata.vertices.request(); } void _batch_render_polys(const Batch &p_batch, RasterizerStorageGLES2::Material *p_material);
Batch *_batch_request_new(bool p_blank = true); void _batch_render_lines(const Batch &p_batch, RasterizerStorageGLES2::Material *p_material, bool p_anti_alias);
bool _detect_batch_break(Item *p_ci); // funcs used from rasterizer_canvas_batcher template
void _software_transform_vertex(BatchVector2 &r_v, const Transform2D &p_tr) const; void gl_enable_scissor(int p_x, int p_y, int p_width, int p_height) const;
void _software_transform_vertex(Vector2 &r_v, const Transform2D &p_tr) const; void gl_disable_scissor() const;
TransformMode _find_transform_mode(const Transform2D &p_tr) const;
void _prefill_default_batch(FillState &r_fill_state, int p_command_num, const Item &p_item);
// sorting
void sort_items();
bool sort_items_from(int p_start);
bool _sort_items_match(const BSortItem &p_a, const BSortItem &p_b) const;
// light scissoring
bool _light_find_intersection(const Rect2 &p_item_rect, const Transform2D &p_light_xform, const Rect2 &p_light_rect, Rect2 &r_cliprect) const;
bool _light_scissor_begin(const Rect2 &p_item_rect, const Transform2D &p_light_xform, const Rect2 &p_light_rect) const;
void _calculate_scissor_threshold_area();
// no need to compile these in in release, they are unneeded outside the editor and only add to executable size
#ifdef DEBUG_ENABLED
void diagnose_batches(Item::Command *const *p_commands);
String get_command_type_string(const Item::Command &p_command) const;
#endif
public: public:
void initialize(); void initialize();
RasterizerCanvasGLES2(); RasterizerCanvasGLES2();
private:
template <bool SEND_LIGHT_ANGLES>
bool prefill_rect(Item::CommandRect *rect, FillState &r_fill_state, int &r_command_start, int command_num, int command_count, Item::Command *const *commands, Item *p_item, bool multiply_final_modulate);
template <class BATCH_VERTEX_TYPE, bool INCLUDE_LIGHT_ANGLES>
void _translate_batches_to_larger_FVF();
}; };
//////////////////////////////////////////////////////////////
// Default batches will not occur in software transform only items
// EXCEPT IN THE CASE OF SINGLE RECTS (and this may well not occur, check the logic in prefill_join_item TYPE_RECT)
// but can occur where transform commands have been sent during hardware batch
inline void RasterizerCanvasGLES2::_prefill_default_batch(FillState &r_fill_state, int p_command_num, const Item &p_item) {
if (r_fill_state.curr_batch->type == Batch::BT_DEFAULT) {
// don't need to flush an extra transform command?
if (!r_fill_state.transform_extra_command_number_p1) {
// another default command, just add to the existing batch
r_fill_state.curr_batch->num_commands++;
} else {
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
if (r_fill_state.transform_extra_command_number_p1 != p_command_num) {
WARN_PRINT_ONCE("_prefill_default_batch : transform_extra_command_number_p1 != p_command_num");
}
#endif
// if the first member of the batch is a transform we have to be careful
if (!r_fill_state.curr_batch->num_commands) {
// there can be leading useless extra transforms (sometimes happens with debug collision polys)
// we need to rejig the first_command for the first useful transform
r_fill_state.curr_batch->first_command += r_fill_state.transform_extra_command_number_p1 - 1;
}
// we do have a pending extra transform command to flush
// either the extra transform is in the prior command, or not, in which case we need 2 batches
r_fill_state.curr_batch->num_commands += 2;
r_fill_state.transform_extra_command_number_p1 = 0; // mark as sent
r_fill_state.extra_matrix_sent = true;
// the original mode should always be hardware transform ..
// test this assumption
//CRASH_COND(r_fill_state.orig_transform_mode != TM_NONE);
r_fill_state.transform_mode = r_fill_state.orig_transform_mode;
// do we need to restore anything else?
}
} else {
// end of previous different type batch, so start new default batch
// first consider whether there is a dirty extra matrix to send
if (r_fill_state.transform_extra_command_number_p1) {
// get which command the extra is in, and blank all the records as it no longer is stored CPU side
int extra_command = r_fill_state.transform_extra_command_number_p1 - 1; // plus 1 based
r_fill_state.transform_extra_command_number_p1 = 0;
r_fill_state.extra_matrix_sent = true;
// send the extra to the GPU in a batch
r_fill_state.curr_batch = _batch_request_new();
r_fill_state.curr_batch->type = Batch::BT_DEFAULT;
r_fill_state.curr_batch->first_command = extra_command;
r_fill_state.curr_batch->num_commands = 1;
// revert to the original transform mode
// e.g. go back to NONE if we were in hardware transform mode
r_fill_state.transform_mode = r_fill_state.orig_transform_mode;
// reset the original transform if we are going back to software mode,
// because the extra is now done on the GPU...
// (any subsequent extras are sent directly to the GPU, no deferring)
if (r_fill_state.orig_transform_mode != TM_NONE) {
r_fill_state.transform_combined = p_item.final_transform;
}
// can possibly combine batch with the next one in some cases
// this is more efficient than having an extra batch especially for the extra
if ((extra_command + 1) == p_command_num) {
r_fill_state.curr_batch->num_commands = 2;
return;
}
}
// start default batch
r_fill_state.curr_batch = _batch_request_new();
r_fill_state.curr_batch->type = Batch::BT_DEFAULT;
r_fill_state.curr_batch->first_command = p_command_num;
r_fill_state.curr_batch->num_commands = 1;
}
}
inline void RasterizerCanvasGLES2::_software_transform_vertex(BatchVector2 &r_v, const Transform2D &p_tr) const {
Vector2 vc(r_v.x, r_v.y);
vc = p_tr.xform(vc);
r_v.set(vc);
}
inline void RasterizerCanvasGLES2::_software_transform_vertex(Vector2 &r_v, const Transform2D &p_tr) const {
r_v = p_tr.xform(r_v);
}
inline RasterizerCanvasGLES2::TransformMode RasterizerCanvasGLES2::_find_transform_mode(const Transform2D &p_tr) const {
// decided whether to do translate only for software transform
if ((p_tr.elements[0].x == 1.0) &&
(p_tr.elements[0].y == 0.0) &&
(p_tr.elements[1].x == 0.0) &&
(p_tr.elements[1].y == 1.0)) {
return TM_TRANSLATE;
}
return TM_ALL;
}
inline bool RasterizerCanvasGLES2::_sort_items_match(const BSortItem &p_a, const BSortItem &p_b) const {
const Item *a = p_a.item;
const Item *b = p_b.item;
if (b->commands.size() != 1)
return false;
// tested outside function
// if (a->commands.size() != 1)
// return false;
const Item::Command &cb = *b->commands[0];
if (cb.type != Item::Command::TYPE_RECT)
return false;
const Item::Command &ca = *a->commands[0];
// tested outside function
// if (ca.type != Item::Command::TYPE_RECT)
// return false;
const Item::CommandRect *rect_a = static_cast<const Item::CommandRect *>(&ca);
const Item::CommandRect *rect_b = static_cast<const Item::CommandRect *>(&cb);
if (rect_a->texture != rect_b->texture)
return false;
return true;
}
//////////////////////////////////////////////////////////////
// TEMPLATE FUNCS
// Translation always involved adding color to the FVF, which enables
// joining of batches that have different colors.
// There is a trade off. Non colored verts are smaller so work faster, but
// there comes a point where it is better to just use colored verts to avoid lots of
// batches.
// In addition this can optionally add light angles to the FVF, necessary for normal mapping.
template <class BATCH_VERTEX_TYPE, bool INCLUDE_LIGHT_ANGLES>
void RasterizerCanvasGLES2::_translate_batches_to_larger_FVF() {
// zeros the size and sets up how big each unit is
bdata.unit_vertices.prepare(sizeof(BATCH_VERTEX_TYPE));
bdata.batches_temp.reset();
// As the vertices_colored and batches_temp are 'mirrors' of the non-colored version,
// the sizes should be equal, and allocations should never fail. Hence the use of debug
// asserts to check program flow, these should not occur at runtime unless the allocation
// code has been altered.
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
CRASH_COND(bdata.unit_vertices.max_size() != bdata.vertices.max_size());
CRASH_COND(bdata.batches_temp.max_size() != bdata.batches.max_size());
#endif
Color curr_col(-1.0, -1.0, -1.0, -1.0);
Batch *dest_batch = 0;
const float *source_light_angles = &bdata.light_angles[0];
// translate the batches into vertex colored batches
for (int n = 0; n < bdata.batches.size(); n++) {
const Batch &source_batch = bdata.batches[n];
// does source batch use light angles?
const BatchTex &btex = bdata.batch_textures[source_batch.batch_texture_id];
bool source_batch_uses_light_angles = btex.RID_normal != RID();
bool needs_new_batch = true;
if (dest_batch) {
if (dest_batch->type == source_batch.type) {
if (source_batch.type == Batch::BT_RECT) {
if (dest_batch->batch_texture_id == source_batch.batch_texture_id) {
// add to previous batch
dest_batch->num_commands += source_batch.num_commands;
needs_new_batch = false;
// create the colored verts (only if not default)
int first_vert = source_batch.first_quad * 4;
int end_vert = 4 * (source_batch.first_quad + source_batch.num_commands);
for (int v = first_vert; v < end_vert; v++) {
const BatchVertex &bv = bdata.vertices[v];
BATCH_VERTEX_TYPE *cv = (BatchVertexLightAngled *)bdata.unit_vertices.request();
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
CRASH_COND(!cv);
#endif
cv->pos = bv.pos;
cv->uv = bv.uv;
cv->col = source_batch.color;
if (INCLUDE_LIGHT_ANGLES) {
// this is required to allow compilation with non light angle vertex.
// it should be compiled out.
BatchVertexLightAngled *lv = (BatchVertexLightAngled *)cv;
if (source_batch_uses_light_angles)
lv->light_angle = *source_light_angles++;
else
lv->light_angle = 0.0f; // dummy, unused in vertex shader (could possibly be left uninitialized, but probably bad idea)
}
}
} // textures match
} else {
// default
// we can still join, but only under special circumstances
// does this ever happen? not sure at this stage, but left for future expansion
uint32_t source_last_command = source_batch.first_command + source_batch.num_commands;
if (source_last_command == dest_batch->first_command) {
dest_batch->num_commands += source_batch.num_commands;
needs_new_batch = false;
} // if the commands line up exactly
}
} // if both batches are the same type
} // if dest batch is valid
if (needs_new_batch) {
dest_batch = bdata.batches_temp.request();
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
CRASH_COND(!dest_batch);
#endif
*dest_batch = source_batch;
// create the colored verts (only if not default)
if (source_batch.type != Batch::BT_DEFAULT) {
int first_vert = source_batch.first_quad * 4;
int end_vert = 4 * (source_batch.first_quad + source_batch.num_commands);
for (int v = first_vert; v < end_vert; v++) {
const BatchVertex &bv = bdata.vertices[v];
BATCH_VERTEX_TYPE *cv = (BatchVertexLightAngled *)bdata.unit_vertices.request();
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
CRASH_COND(!cv);
#endif
cv->pos = bv.pos;
cv->uv = bv.uv;
cv->col = source_batch.color;
if (INCLUDE_LIGHT_ANGLES) {
// this is required to allow compilation with non light angle vertex.
// it should be compiled out.
BatchVertexLightAngled *lv = (BatchVertexLightAngled *)cv;
if (source_batch_uses_light_angles)
lv->light_angle = *source_light_angles++;
else
lv->light_angle = 0.0f; // dummy, unused in vertex shader (could possibly be left uninitialized, but probably bad idea)
} // if using light angles
}
}
}
}
// copy the temporary batches to the master batch list (this could be avoided but it makes the code cleaner)
bdata.batches.copy_from(bdata.batches_temp);
}
// return true if buffer full up, else return false
template <bool SEND_LIGHT_ANGLES>
bool RasterizerCanvasGLES2::prefill_rect(Item::CommandRect *rect, FillState &r_fill_state, int &r_command_start, int command_num, int command_count, Item::Command *const *commands, Item *p_item, bool multiply_final_modulate) {
bool change_batch = false;
// conditions for creating a new batch
if (r_fill_state.curr_batch->type != Batch::BT_RECT) {
change_batch = true;
// check for special case if there is only a single or small number of rects,
// in which case we will use the legacy default rect renderer
// because it is faster for single rects
// we only want to do this if not a joined item with more than 1 item,
// because joined items with more than 1, the command * will be incorrect
// NOTE - this is assuming that use_hardware_transform means that it is a non-joined item!!
// If that assumption is incorrect this will go horribly wrong.
if (bdata.settings_use_single_rect_fallback && r_fill_state.use_hardware_transform) {
bool is_single_rect = false;
int command_num_next = command_num + 1;
if (command_num_next < command_count) {
Item::Command *command_next = commands[command_num_next];
if ((command_next->type != Item::Command::TYPE_RECT) && (command_next->type != Item::Command::TYPE_TRANSFORM)) {
is_single_rect = true;
}
} else {
is_single_rect = true;
}
// if it is a rect on its own, do exactly the same as the default routine
if (is_single_rect) {
_prefill_default_batch(r_fill_state, command_num, *p_item);
return false;
}
} // if use hardware transform
}
Color col = rect->modulate;
if (multiply_final_modulate) {
col *= r_fill_state.final_modulate;
}
// instead of doing all the texture preparation for EVERY rect,
// we build a list of texture combinations and do this once off.
// This means we have a potentially rather slow step to identify which texture combo
// using the RIDs.
int old_batch_tex_id = r_fill_state.batch_tex_id;
r_fill_state.batch_tex_id = _batch_find_or_create_tex(rect->texture, rect->normal_map, rect->flags & CANVAS_RECT_TILE, old_batch_tex_id);
//r_fill_state.use_light_angles = send_light_angles;
if (SEND_LIGHT_ANGLES)
bdata.use_light_angles = true;
// try to create vertices BEFORE creating a batch,
// because if the vertex buffer is full, we need to finish this
// function, draw what we have so far, and then start a new set of batches
// request FOUR vertices at a time, this is more efficient
BatchVertex *bvs = bdata.vertices.request(4);
if (!bvs) {
// run out of space in the vertex buffer .. finish this function and draw what we have so far
// return where we got to
r_command_start = command_num;
return true;
}
// conditions for creating a new batch
if (old_batch_tex_id != r_fill_state.batch_tex_id) {
change_batch = true;
}
// we need to treat color change separately because we need to count these
// to decide whether to switch on the fly to colored vertices.
if (!r_fill_state.curr_batch->color.equals(col)) {
change_batch = true;
bdata.total_color_changes++;
}
if (change_batch) {
// put the tex pixel size in a local (less verbose and can be a register)
const BatchTex &batchtex = bdata.batch_textures[r_fill_state.batch_tex_id];
batchtex.tex_pixel_size.to(r_fill_state.texpixel_size);
if (bdata.settings_uv_contract) {
r_fill_state.contract_uvs = (batchtex.flags & VS::TEXTURE_FLAG_FILTER) == 0;
}
// need to preserve texpixel_size between items
r_fill_state.texpixel_size = r_fill_state.texpixel_size;
// open new batch (this should never fail, it dynamically grows)
r_fill_state.curr_batch = _batch_request_new(false);
r_fill_state.curr_batch->type = Batch::BT_RECT;
r_fill_state.curr_batch->color.set(col);
r_fill_state.curr_batch->batch_texture_id = r_fill_state.batch_tex_id;
r_fill_state.curr_batch->first_command = command_num;
r_fill_state.curr_batch->num_commands = 1;
r_fill_state.curr_batch->first_quad = bdata.total_quads;
} else {
// we could alternatively do the count when closing a batch .. perhaps more efficient
r_fill_state.curr_batch->num_commands++;
}
// fill the quad geometry
Vector2 mins = rect->rect.position;
if (r_fill_state.transform_mode == TM_TRANSLATE) {
_software_transform_vertex(mins, r_fill_state.transform_combined);
}
Vector2 maxs = mins + rect->rect.size;
// just aliases
BatchVertex *bA = &bvs[0];
BatchVertex *bB = &bvs[1];
BatchVertex *bC = &bvs[2];
BatchVertex *bD = &bvs[3];
bA->pos.x = mins.x;
bA->pos.y = mins.y;
bB->pos.x = maxs.x;
bB->pos.y = mins.y;
bC->pos.x = maxs.x;
bC->pos.y = maxs.y;
bD->pos.x = mins.x;
bD->pos.y = maxs.y;
// possibility of applying flips here for normal mapping .. but they don't seem to be used
if (rect->rect.size.x < 0) {
SWAP(bA->pos, bB->pos);
SWAP(bC->pos, bD->pos);
}
if (rect->rect.size.y < 0) {
SWAP(bA->pos, bD->pos);
SWAP(bB->pos, bC->pos);
}
if (r_fill_state.transform_mode == TM_ALL) {
_software_transform_vertex(bA->pos, r_fill_state.transform_combined);
_software_transform_vertex(bB->pos, r_fill_state.transform_combined);
_software_transform_vertex(bC->pos, r_fill_state.transform_combined);
_software_transform_vertex(bD->pos, r_fill_state.transform_combined);
}
// uvs
Vector2 src_min;
Vector2 src_max;
if (rect->flags & CANVAS_RECT_REGION) {
src_min = rect->source.position;
src_max = src_min + rect->source.size;
src_min *= r_fill_state.texpixel_size;
src_max *= r_fill_state.texpixel_size;
const float uv_epsilon = bdata.settings_uv_contract_amount;
// nudge offset for the maximum to prevent precision error on GPU reading into line outside the source rect
// this is very difficult to get right.
if (r_fill_state.contract_uvs) {
src_min.x += uv_epsilon;
src_min.y += uv_epsilon;
src_max.x -= uv_epsilon;
src_max.y -= uv_epsilon;
}
} else {
src_min = Vector2(0, 0);
src_max = Vector2(1, 1);
}
// 10% faster calculating the max first
Vector2 uvs[4] = {
src_min,
Vector2(src_max.x, src_min.y),
src_max,
Vector2(src_min.x, src_max.y),
};
// for encoding in light angle
// flips should be optimized out when not being used for light angle.
bool flip_h = false;
bool flip_v = false;
if (rect->flags & CANVAS_RECT_TRANSPOSE) {
SWAP(uvs[1], uvs[3]);
}
if (rect->flags & CANVAS_RECT_FLIP_H) {
SWAP(uvs[0], uvs[1]);
SWAP(uvs[2], uvs[3]);
flip_h = !flip_h;
flip_v = !flip_v;
}
if (rect->flags & CANVAS_RECT_FLIP_V) {
SWAP(uvs[0], uvs[3]);
SWAP(uvs[1], uvs[2]);
flip_v = !flip_v;
}
bA->uv.set(uvs[0]);
bB->uv.set(uvs[1]);
bC->uv.set(uvs[2]);
bD->uv.set(uvs[3]);
if (SEND_LIGHT_ANGLES) {
// we can either keep the light angles in sync with the verts when writing,
// or sync them up during translation. We are syncing in translation.
// N.B. There may be batches that don't require light_angles between batches that do.
float *angles = bdata.light_angles.request(4);
#if defined(TOOLS_ENABLED) && defined(DEBUG_ENABLED)
CRASH_COND(angles == nullptr);
#endif
float angle = 0.0f;
const float TWO_PI = Math_PI * 2;
if (r_fill_state.transform_mode != TM_NONE) {
const Transform2D &tr = r_fill_state.transform_combined;
// apply to an x axis
// the x axis and y axis can be taken directly from the transform (no need to xform identity vectors)
Vector2 x_axis(tr.elements[0][0], tr.elements[1][0]);
// have to do a y axis to check for scaling flips
// this is hassle and extra slowness. We could only allow flips via the flags.
Vector2 y_axis(tr.elements[0][1], tr.elements[1][1]);
// has the x / y axis flipped due to scaling?
float cross = x_axis.cross(y_axis);
if (cross < 0.0f) {
flip_v = !flip_v;
}
// passing an angle is smaller than a vector, it can be reconstructed in the shader
angle = x_axis.angle();
// we don't want negative angles, as negative is used to encode flips.
// This moves range from -PI to PI to 0 to TWO_PI
if (angle < 0.0f)
angle += TWO_PI;
} // if transform needed
// if horizontal flip, angle is shifted by 180 degrees
if (flip_h) {
angle += Math_PI;
// mod to get back to 0 to TWO_PI range
angle = fmodf(angle, TWO_PI);
}
// add 1 (to take care of zero floating point error with sign)
angle += 1.0f;
// flip if necessary to indicate a vertical flip in the shader
if (flip_v)
angle *= -1.0f;
// light angle must be sent for each vert, instead as a single uniform in the uniform draw method
// this has the benefit of enabling batching with light angles.
for (int n = 0; n < 4; n++) {
angles[n] = angle;
}
}
// increment quad count
bdata.total_quads++;
return false;
}
#endif // RASTERIZERCANVASGLES2_H #endif // RASTERIZERCANVASGLES2_H

View file

@ -483,6 +483,42 @@ void RasterizerGLES2::make_current() {
void RasterizerGLES2::register_config() { void RasterizerGLES2::register_config() {
} }
// returns NULL if no error, or an error string
const char *RasterizerGLES2::gl_check_for_error(bool p_print_error) {
GLenum err = glGetError();
const char *err_string = nullptr;
switch (err) {
default: {
// not recognised
} break;
case GL_NO_ERROR: {
} break;
case GL_INVALID_ENUM: {
err_string = "GL_INVALID_ENUM";
} break;
case GL_INVALID_VALUE: {
err_string = "GL_INVALID_VALUE";
} break;
case GL_INVALID_OPERATION: {
err_string = "GL_INVALID_OPERATION";
} break;
case GL_INVALID_FRAMEBUFFER_OPERATION: {
err_string = "GL_INVALID_FRAMEBUFFER_OPERATION";
} break;
case GL_OUT_OF_MEMORY: {
err_string = "GL_OUT_OF_MEMORY";
} break;
}
if (p_print_error && err_string) {
print_line(err_string);
}
return err_string;
}
RasterizerGLES2::RasterizerGLES2() { RasterizerGLES2::RasterizerGLES2() {
storage = memnew(RasterizerStorageGLES2); storage = memnew(RasterizerStorageGLES2);

View file

@ -71,6 +71,8 @@ public:
virtual bool is_low_end() const { return true; } virtual bool is_low_end() const { return true; }
virtual const char *gl_check_for_error(bool p_print_error = true);
RasterizerGLES2(); RasterizerGLES2();
~RasterizerGLES2(); ~RasterizerGLES2();
}; };

View file

@ -1452,6 +1452,7 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const {
shaders.actions_canvas.usage_flag_pointers["TIME"] = &p_shader->canvas_item.uses_time; shaders.actions_canvas.usage_flag_pointers["TIME"] = &p_shader->canvas_item.uses_time;
shaders.actions_canvas.usage_flag_pointers["MODULATE"] = &p_shader->canvas_item.uses_modulate; shaders.actions_canvas.usage_flag_pointers["MODULATE"] = &p_shader->canvas_item.uses_modulate;
shaders.actions_canvas.usage_flag_pointers["COLOR"] = &p_shader->canvas_item.uses_color; shaders.actions_canvas.usage_flag_pointers["COLOR"] = &p_shader->canvas_item.uses_color;
shaders.actions_canvas.usage_flag_pointers["VERTEX"] = &p_shader->canvas_item.uses_vertex; shaders.actions_canvas.usage_flag_pointers["VERTEX"] = &p_shader->canvas_item.uses_vertex;
actions = &shaders.actions_canvas; actions = &shaders.actions_canvas;
@ -1552,10 +1553,10 @@ void RasterizerStorageGLES2::_update_shader(Shader *p_shader) const {
// some logic for batching // some logic for batching
if (p_shader->mode == VS::SHADER_CANVAS_ITEM) { if (p_shader->mode == VS::SHADER_CANVAS_ITEM) {
if (p_shader->canvas_item.uses_modulate | p_shader->canvas_item.uses_color) { if (p_shader->canvas_item.uses_modulate | p_shader->canvas_item.uses_color) {
p_shader->canvas_item.batch_flags |= Shader::CanvasItem::PREVENT_COLOR_BAKING; p_shader->canvas_item.batch_flags |= RasterizerStorageCommon::PREVENT_COLOR_BAKING;
} }
if (p_shader->canvas_item.uses_vertex) { if (p_shader->canvas_item.uses_vertex) {
p_shader->canvas_item.batch_flags |= Shader::CanvasItem::PREVENT_VERTEX_BAKING; p_shader->canvas_item.batch_flags |= RasterizerStorageCommon::PREVENT_VERTEX_BAKING;
} }
} }
@ -3787,14 +3788,17 @@ void RasterizerStorageGLES2::_update_skeleton_transform_buffer(const PoolVector<
glBindBuffer(GL_ARRAY_BUFFER, resources.skeleton_transform_buffer); glBindBuffer(GL_ARRAY_BUFFER, resources.skeleton_transform_buffer);
uint32_t buffer_size = p_size * sizeof(float);
if (p_size > resources.skeleton_transform_buffer_size) { if (p_size > resources.skeleton_transform_buffer_size) {
// new requested buffer is bigger, so resizing the GPU buffer // new requested buffer is bigger, so resizing the GPU buffer
resources.skeleton_transform_buffer_size = p_size; resources.skeleton_transform_buffer_size = p_size;
glBufferData(GL_ARRAY_BUFFER, p_size * sizeof(float), p_data.read().ptr(), GL_DYNAMIC_DRAW); glBufferData(GL_ARRAY_BUFFER, buffer_size, p_data.read().ptr(), GL_DYNAMIC_DRAW);
} else { } else {
glBufferSubData(GL_ARRAY_BUFFER, 0, p_size * sizeof(float), p_data.read().ptr()); // this may not be best, it could be better to use glBufferData in both cases.
buffer_orphan_and_upload(resources.skeleton_transform_buffer_size, 0, buffer_size, p_data.read().ptr());
} }
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);

View file

@ -40,11 +40,6 @@
#include "shaders/copy.glsl.gen.h" #include "shaders/copy.glsl.gen.h"
#include "shaders/cubemap_filter.glsl.gen.h" #include "shaders/cubemap_filter.glsl.gen.h"
/*
#include "shaders/blend_shape.glsl.gen.h"
#include "shaders/canvas.glsl.gen.h"
#include "shaders/particles.glsl.gen.h"
*/
class RasterizerCanvasGLES2; class RasterizerCanvasGLES2;
class RasterizerSceneGLES2; class RasterizerSceneGLES2;
@ -450,10 +445,7 @@ public:
// these flags are specifically for batching // these flags are specifically for batching
// some of the logic is thus in rasterizer_storage.cpp // some of the logic is thus in rasterizer_storage.cpp
// we could alternatively set bitflags for each 'uses' and test on the fly // we could alternatively set bitflags for each 'uses' and test on the fly
enum BatchFlags { // defined in RasterizerStorageCommon::BatchFlags
PREVENT_COLOR_BAKING = 1 << 0,
PREVENT_VERTEX_BAKING = 1 << 1,
};
unsigned int batch_flags; unsigned int batch_flags;
bool uses_screen_texture; bool uses_screen_texture;

View file

@ -19,7 +19,7 @@ uniform highp mat4 modelview_matrix;
uniform highp mat4 extra_matrix; uniform highp mat4 extra_matrix;
attribute highp vec2 vertex; // attrib:0 attribute highp vec2 vertex; // attrib:0
#ifdef USE_LIGHT_ANGLE #ifdef USE_ATTRIB_LIGHT_ANGLE
// shared with tangent, not used in canvas shader // shared with tangent, not used in canvas shader
attribute highp float light_angle; // attrib:2 attribute highp float light_angle; // attrib:2
#endif #endif
@ -27,6 +27,16 @@ attribute highp float light_angle; // attrib:2
attribute vec4 color_attrib; // attrib:3 attribute vec4 color_attrib; // attrib:3
attribute vec2 uv_attrib; // attrib:4 attribute vec2 uv_attrib; // attrib:4
#ifdef USE_ATTRIB_MODULATE
attribute highp vec4 modulate_attrib; // attrib:5
#endif
#ifdef USE_ATTRIB_LARGE_VERTEX
// shared with skeleton attributes, not used in batched shader
attribute highp vec2 translate_attrib; // attrib:6
attribute highp vec4 basis_attrib; // attrib:7
#endif
#ifdef USE_SKELETON #ifdef USE_SKELETON
attribute highp vec4 bone_indices; // attrib:6 attribute highp vec4 bone_indices; // attrib:6
attribute highp vec4 bone_weights; // attrib:7 attribute highp vec4 bone_weights; // attrib:7
@ -54,6 +64,12 @@ uniform highp mat4 skeleton_transform_inverse;
varying vec2 uv_interp; varying vec2 uv_interp;
varying vec4 color_interp; varying vec4 color_interp;
#ifdef USE_ATTRIB_MODULATE
// modulate doesn't need interpolating but we need to send it to the fragment shader
varying vec4 modulate_interp;
#endif
#ifdef MODULATE_USED #ifdef MODULATE_USED
uniform vec4 final_modulate; uniform vec4 final_modulate;
#endif #endif
@ -171,6 +187,24 @@ VERTEX_SHADER_CODE
gl_PointSize = point_size; gl_PointSize = point_size;
#ifdef USE_ATTRIB_MODULATE
// modulate doesn't need interpolating but we need to send it to the fragment shader
modulate_interp = modulate_attrib;
#endif
#ifdef USE_ATTRIB_LARGE_VERTEX
// transform is in attributes
vec2 temp;
temp = outvec.xy;
temp.x = (outvec.x * basis_attrib.x) + (outvec.y * basis_attrib.z);
temp.y = (outvec.x * basis_attrib.y) + (outvec.y * basis_attrib.w);
temp += translate_attrib;
outvec.xy = temp;
#endif
#if !defined(SKIP_TRANSFORM_USED) #if !defined(SKIP_TRANSFORM_USED)
outvec = extra_matrix_instance * outvec; outvec = extra_matrix_instance * outvec;
outvec = modelview_matrix * outvec; outvec = modelview_matrix * outvec;
@ -225,7 +259,7 @@ VERTEX_SHADER_CODE
pos = outvec.xy; pos = outvec.xy;
#endif #endif
#ifdef USE_LIGHT_ANGLE #ifdef USE_ATTRIB_LIGHT_ANGLE
// we add a fixed offset because we are using the sign later, // we add a fixed offset because we are using the sign later,
// and don't want floating point error around 0.0 // and don't want floating point error around 0.0
float la = abs(light_angle) - 1.0; float la = abs(light_angle) - 1.0;
@ -303,6 +337,10 @@ uniform mediump sampler2D normal_texture; // texunit:-2
varying mediump vec2 uv_interp; varying mediump vec2 uv_interp;
varying mediump vec4 color_interp; varying mediump vec4 color_interp;
#ifdef USE_ATTRIB_MODULATE
varying mediump vec4 modulate_interp;
#endif
uniform highp float time; uniform highp float time;
uniform vec4 final_modulate; uniform vec4 final_modulate;
@ -442,6 +480,11 @@ FRAGMENT_SHADER_CODE
color *= final_modulate; color *= final_modulate;
#endif #endif
#ifdef USE_ATTRIB_MODULATE
// todo .. this won't be used at the same time as MODULATE_USED
color *= modulate_interp;
#endif
#ifdef USE_LIGHTING #ifdef USE_LIGHTING
vec2 light_vec = transformed_light_uv; vec2 light_vec = transformed_light_uv;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,163 @@
/*************************************************************************/
/* rasterizer_canvas_base_gles3.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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 RASTERIZERCANVASBASEGLES3_H
#define RASTERIZERCANVASBASEGLES3_H
#include "rasterizer_storage_gles3.h"
#include "servers/visual/rasterizer.h"
#include "shaders/canvas_shadow.glsl.gen.h"
#include "shaders/lens_distorted.glsl.gen.h"
class RasterizerSceneGLES3;
class RasterizerCanvasBaseGLES3 : public RasterizerCanvas {
public:
struct CanvasItemUBO {
float projection_matrix[16];
float time;
uint8_t padding[12];
};
RasterizerSceneGLES3 *scene_render;
struct Data {
enum { NUM_QUAD_ARRAY_VARIATIONS = 8 };
GLuint canvas_quad_vertices;
GLuint canvas_quad_array;
GLuint polygon_buffer;
GLuint polygon_buffer_quad_arrays[NUM_QUAD_ARRAY_VARIATIONS];
GLuint polygon_buffer_pointer_array;
GLuint polygon_index_buffer;
GLuint particle_quad_vertices;
GLuint particle_quad_array;
uint32_t polygon_buffer_size;
uint32_t polygon_index_buffer_size;
} data;
struct State {
CanvasItemUBO canvas_item_ubo_data;
GLuint canvas_item_ubo;
bool canvas_texscreen_used;
CanvasShaderGLES3 canvas_shader;
CanvasShadowShaderGLES3 canvas_shadow_shader;
LensDistortedShaderGLES3 lens_shader;
bool using_texture_rect;
bool using_ninepatch;
bool using_light_angle;
bool using_modulate;
bool using_large_vertex;
RID current_tex;
RID current_normal;
RasterizerStorageGLES3::Texture *current_tex_ptr;
Transform vp;
Color canvas_item_modulate;
Transform2D extra_matrix;
Transform2D final_transform;
bool using_skeleton;
Transform2D skeleton_transform;
Transform2D skeleton_transform_inverse;
} state;
RasterizerStorageGLES3 *storage;
struct LightInternal : public RID_Data {
struct UBOData {
float light_matrix[16];
float local_matrix[16];
float shadow_matrix[16];
float color[4];
float shadow_color[4];
float light_pos[2];
float shadowpixel_size;
float shadow_gradient;
float light_height;
float light_outside_alpha;
float shadow_distance_mult;
uint8_t padding[4];
} ubo_data;
GLuint ubo;
};
RID_Owner<LightInternal> light_internal_owner;
virtual RID light_internal_create();
virtual void light_internal_update(RID p_rid, Light *p_light);
virtual void light_internal_free(RID p_rid);
virtual void canvas_begin();
virtual void canvas_end();
void _set_texture_rect_mode(bool p_enable, bool p_ninepatch = false, bool p_light_angle = false, bool p_modulate = false, bool p_large_vertex = false);
RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map, bool p_force = false);
void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs, const float *p_light_angles = nullptr);
void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor, const int *p_bones, const float *p_weights);
void _draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
void _draw_generic_indices(GLuint p_primitive, const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor);
void _copy_texscreen(const Rect2 &p_rect);
virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow);
virtual void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache);
virtual void reset_canvas();
void draw_generic_textured_rect(const Rect2 &p_rect, const Rect2 &p_src);
void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample);
void render_rect_nvidia_workaround(const Item::CommandRect *p_rect, const RasterizerStorageGLES3::Texture *p_texture);
void initialize();
void finalize();
virtual void draw_window_margins(int *black_margin, RID *black_image);
RasterizerCanvasBaseGLES3();
};
#endif // RASTERIZERCANVASBASEGLES3_H

File diff suppressed because it is too large Load diff

View file

@ -31,132 +31,49 @@
#ifndef RASTERIZERCANVASGLES3_H #ifndef RASTERIZERCANVASGLES3_H
#define RASTERIZERCANVASGLES3_H #define RASTERIZERCANVASGLES3_H
#include "rasterizer_storage_gles3.h" #include "drivers/gles_common/rasterizer_canvas_batcher.h"
#include "servers/visual/rasterizer.h" #include "rasterizer_canvas_base_gles3.h"
#include "shaders/canvas_shadow.glsl.gen.h" class RasterizerCanvasGLES3 : public RasterizerCanvasBaseGLES3, public RasterizerCanvasBatcher<RasterizerCanvasGLES3, RasterizerStorageGLES3> {
#include "shaders/lens_distorted.glsl.gen.h" friend class RasterizerCanvasBatcher<RasterizerCanvasGLES3, RasterizerStorageGLES3>;
class RasterizerSceneGLES3; private:
struct BatchGLData {
// for batching
GLuint batch_vertex_array[5];
} batch_gl_data;
class RasterizerCanvasGLES3 : public RasterizerCanvas {
public: public:
struct CanvasItemUBO { virtual void canvas_render_items_begin(const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
virtual void canvas_render_items_end();
float projection_matrix[16]; virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
float time;
uint8_t padding[12];
};
RasterizerSceneGLES3 *scene_render;
struct Data {
enum { NUM_QUAD_ARRAY_VARIATIONS = 8 };
GLuint canvas_quad_vertices;
GLuint canvas_quad_array;
GLuint polygon_buffer;
GLuint polygon_buffer_quad_arrays[NUM_QUAD_ARRAY_VARIATIONS];
GLuint polygon_buffer_pointer_array;
GLuint polygon_index_buffer;
GLuint particle_quad_vertices;
GLuint particle_quad_array;
uint32_t polygon_buffer_size;
uint32_t polygon_index_buffer_size;
} data;
struct State {
CanvasItemUBO canvas_item_ubo_data;
GLuint canvas_item_ubo;
bool canvas_texscreen_used;
CanvasShaderGLES3 canvas_shader;
CanvasShadowShaderGLES3 canvas_shadow_shader;
LensDistortedShaderGLES3 lens_shader;
bool using_texture_rect;
bool using_ninepatch;
bool using_light_angle;
RID current_tex;
RID current_normal;
RasterizerStorageGLES3::Texture *current_tex_ptr;
Transform vp;
Color canvas_item_modulate;
Transform2D extra_matrix;
Transform2D final_transform;
bool using_skeleton;
Transform2D skeleton_transform;
Transform2D skeleton_transform_inverse;
} state;
RasterizerStorageGLES3 *storage;
bool use_nvidia_rect_workaround;
struct LightInternal : public RID_Data {
struct UBOData {
float light_matrix[16];
float local_matrix[16];
float shadow_matrix[16];
float color[4];
float shadow_color[4];
float light_pos[2];
float shadowpixel_size;
float shadow_gradient;
float light_height;
float light_outside_alpha;
float shadow_distance_mult;
uint8_t padding[4];
} ubo_data;
GLuint ubo;
};
RID_Owner<LightInternal> light_internal_owner;
virtual RID light_internal_create();
virtual void light_internal_update(RID p_rid, Light *p_light);
virtual void light_internal_free(RID p_rid);
virtual void canvas_begin(); virtual void canvas_begin();
virtual void canvas_end(); virtual void canvas_end();
_FORCE_INLINE_ void _set_texture_rect_mode(bool p_enable, bool p_ninepatch = false, bool p_light_angle = false); private:
_FORCE_INLINE_ RasterizerStorageGLES3::Texture *_bind_canvas_texture(const RID &p_texture, const RID &p_normal_map, bool p_force = false); // legacy codepath .. to remove after testing
void _legacy_canvas_render_item(Item *p_ci, RenderItemState &r_ris);
_FORCE_INLINE_ void _draw_gui_primitive(int p_points, const Vector2 *p_vertices, const Color *p_colors, const Vector2 *p_uvs, const float *p_light_angles = nullptr); // high level batch funcs
_FORCE_INLINE_ void _draw_polygon(const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor, const int *p_bones, const float *p_weights); void canvas_render_items_implementation(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_base_transform);
_FORCE_INLINE_ void _draw_generic(GLuint p_primitive, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor); void render_joined_item(const BItemJoined &p_bij, RenderItemState &r_ris);
_FORCE_INLINE_ void _draw_generic_indices(GLuint p_primitive, const int *p_indices, int p_index_count, int p_vertex_count, const Vector2 *p_vertices, const Vector2 *p_uvs, const Color *p_colors, bool p_singlecolor); bool try_join_item(Item *p_ci, RenderItemState &r_ris, bool &r_batch_break);
void render_batches(Item::Command *const *p_commands, Item *p_current_clip, bool &r_reclip, RasterizerStorageGLES3::Material *p_material);
_FORCE_INLINE_ void _canvas_item_render_commands(Item *p_item, Item *current_clip, bool &reclip); // low level batch funcs
_FORCE_INLINE_ void _copy_texscreen(const Rect2 &p_rect); void _batch_upload_buffers();
void _batch_render_rects(const Batch &p_batch, RasterizerStorageGLES3::Material *p_material);
void _batch_render_polys(const Batch &p_batch, RasterizerStorageGLES3::Material *p_material);
void _batch_render_lines(const Batch &p_batch, RasterizerStorageGLES3::Material *p_material, bool p_anti_alias);
virtual void canvas_render_items(Item *p_item_list, int p_z, const Color &p_modulate, Light *p_light, const Transform2D &p_transform); // funcs used from rasterizer_canvas_batcher template
virtual void canvas_debug_viewport_shadows(Light *p_lights_with_shadow); void gl_enable_scissor(int p_x, int p_y, int p_width, int p_height) const;
void gl_disable_scissor() const;
virtual void canvas_light_shadow_buffer_update(RID p_buffer, const Transform2D &p_light_xform, int p_light_mask, float p_near, float p_far, LightOccluderInstance *p_occluders, CameraMatrix *p_xform_cache); void gl_checkerror();
virtual void reset_canvas();
void draw_generic_textured_rect(const Rect2 &p_rect, const Rect2 &p_src);
void draw_lens_distortion_rect(const Rect2 &p_rect, float p_k1, float p_k2, const Vector2 &p_eye_center, float p_oversample);
void render_rect_nvidia_workaround(const Item::CommandRect *p_rect, const RasterizerStorageGLES3::Texture *p_texture);
public:
void initialize(); void initialize();
void finalize();
virtual void draw_window_margins(int *black_margin, RID *black_image);
RasterizerCanvasGLES3(); RasterizerCanvasGLES3();
}; };

View file

@ -71,7 +71,7 @@ public:
virtual bool is_low_end() const { return false; } virtual bool is_low_end() const { return false; }
const char *gl_check_for_error(bool p_print_error = true); virtual const char *gl_check_for_error(bool p_print_error = true);
RasterizerGLES3(); RasterizerGLES3();
~RasterizerGLES3(); ~RasterizerGLES3();

View file

@ -2305,6 +2305,10 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const {
p_shader->canvas_item.uses_screen_texture = false; p_shader->canvas_item.uses_screen_texture = false;
p_shader->canvas_item.uses_screen_uv = false; p_shader->canvas_item.uses_screen_uv = false;
p_shader->canvas_item.uses_time = false; p_shader->canvas_item.uses_time = false;
p_shader->canvas_item.uses_modulate = false;
p_shader->canvas_item.uses_color = false;
p_shader->canvas_item.uses_vertex = false;
p_shader->canvas_item.batch_flags = 0;
shaders.actions_canvas.render_mode_values["blend_add"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_ADD); shaders.actions_canvas.render_mode_values["blend_add"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_ADD);
shaders.actions_canvas.render_mode_values["blend_mix"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_MIX); shaders.actions_canvas.render_mode_values["blend_mix"] = Pair<int *, int>(&p_shader->canvas_item.blend_mode, Shader::CanvasItem::BLEND_MODE_MIX);
@ -2321,6 +2325,10 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const {
shaders.actions_canvas.usage_flag_pointers["SCREEN_TEXTURE"] = &p_shader->canvas_item.uses_screen_texture; shaders.actions_canvas.usage_flag_pointers["SCREEN_TEXTURE"] = &p_shader->canvas_item.uses_screen_texture;
shaders.actions_canvas.usage_flag_pointers["TIME"] = &p_shader->canvas_item.uses_time; shaders.actions_canvas.usage_flag_pointers["TIME"] = &p_shader->canvas_item.uses_time;
shaders.actions_canvas.usage_flag_pointers["MODULATE"] = &p_shader->canvas_item.uses_modulate;
shaders.actions_canvas.usage_flag_pointers["COLOR"] = &p_shader->canvas_item.uses_color;
shaders.actions_canvas.usage_flag_pointers["VERTEX"] = &p_shader->canvas_item.uses_vertex;
actions = &shaders.actions_canvas; actions = &shaders.actions_canvas;
actions->uniforms = &p_shader->uniforms; actions->uniforms = &p_shader->uniforms;
@ -2417,6 +2425,16 @@ void RasterizerStorageGLES3::_update_shader(Shader *p_shader) const {
p_shader->uses_vertex_time = gen_code.uses_vertex_time; p_shader->uses_vertex_time = gen_code.uses_vertex_time;
p_shader->uses_fragment_time = gen_code.uses_fragment_time; p_shader->uses_fragment_time = gen_code.uses_fragment_time;
// some logic for batching
if (p_shader->mode == VS::SHADER_CANVAS_ITEM) {
if (p_shader->canvas_item.uses_modulate | p_shader->canvas_item.uses_color) {
p_shader->canvas_item.batch_flags |= RasterizerStorageCommon::PREVENT_COLOR_BAKING;
}
if (p_shader->canvas_item.uses_vertex) {
p_shader->canvas_item.batch_flags |= RasterizerStorageCommon::PREVENT_VERTEX_BAKING;
}
}
//all materials using this shader will have to be invalidated, unfortunately //all materials using this shader will have to be invalidated, unfortunately
for (SelfList<Material> *E = p_shader->materials.first(); E; E = E->next()) { for (SelfList<Material> *E = p_shader->materials.first(); E; E = E->next()) {
@ -5067,7 +5085,8 @@ void RasterizerStorageGLES3::update_dirty_multimeshes() {
if (multimesh->size && multimesh->dirty_data) { if (multimesh->size && multimesh->dirty_data) {
glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer); glBindBuffer(GL_ARRAY_BUFFER, multimesh->buffer);
glBufferSubData(GL_ARRAY_BUFFER, 0, multimesh->data.size() * sizeof(float), multimesh->data.ptr()); uint32_t buffer_size = multimesh->data.size() * sizeof(float);
glBufferData(GL_ARRAY_BUFFER, buffer_size, multimesh->data.ptr(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
} }
@ -8313,6 +8332,9 @@ void RasterizerStorageGLES3::initialize() {
#endif #endif
// not yet detected on GLES3 (is this mandated?)
config.support_npot_repeat_mipmap = true;
config.pvrtc_supported = config.extensions.has("GL_IMG_texture_compression_pvrtc"); config.pvrtc_supported = config.extensions.has("GL_IMG_texture_compression_pvrtc");
config.srgb_decode_supported = config.extensions.has("GL_EXT_texture_sRGB_decode"); config.srgb_decode_supported = config.extensions.has("GL_EXT_texture_sRGB_decode");

View file

@ -84,6 +84,7 @@ public:
bool srgb_decode_supported; bool srgb_decode_supported;
bool support_npot_repeat_mipmap;
bool texture_float_linear_supported; bool texture_float_linear_supported;
bool framebuffer_float_supported; bool framebuffer_float_supported;
bool framebuffer_half_float_supported; bool framebuffer_half_float_supported;
@ -455,9 +456,18 @@ public:
int light_mode; int light_mode;
// these flags are specifically for batching
// some of the logic is thus in rasterizer_storage.cpp
// we could alternatively set bitflags for each 'uses' and test on the fly
// defined in RasterizerStorageCommon::BatchFlags
unsigned int batch_flags;
bool uses_screen_texture; bool uses_screen_texture;
bool uses_screen_uv; bool uses_screen_uv;
bool uses_time; bool uses_time;
bool uses_modulate;
bool uses_color;
bool uses_vertex;
} canvas_item; } canvas_item;

View file

@ -3,13 +3,23 @@
layout(location = 0) in highp vec2 vertex; layout(location = 0) in highp vec2 vertex;
#ifdef USE_LIGHT_ANGLE #ifdef USE_ATTRIB_LIGHT_ANGLE
layout(location = 2) in highp float light_angle; layout(location = 2) in highp float light_angle;
#endif #endif
/* clang-format on */ /* clang-format on */
layout(location = 3) in vec4 color_attrib; layout(location = 3) in vec4 color_attrib;
#ifdef USE_ATTRIB_MODULATE
layout(location = 5) in vec4 modulate_attrib; // attrib:5
#endif
#ifdef USE_ATTRIB_LARGE_VERTEX
// shared with skeleton attributes, not used in batched shader
layout(location = 6) in vec2 translate_attrib; // attrib:6
layout(location = 7) in vec4 basis_attrib; // attrib:7
#endif
#ifdef USE_SKELETON #ifdef USE_SKELETON
layout(location = 6) in uvec4 bone_indices; // attrib:6 layout(location = 6) in uvec4 bone_indices; // attrib:6
layout(location = 7) in vec4 bone_weights; // attrib:7 layout(location = 7) in vec4 bone_weights; // attrib:7
@ -53,6 +63,12 @@ uniform highp mat4 extra_matrix;
out highp vec2 uv_interp; out highp vec2 uv_interp;
out mediump vec4 color_interp; out mediump vec4 color_interp;
#ifdef USE_ATTRIB_MODULATE
// modulate doesn't need interpolating but we need to send it to the fragment shader
out mediump vec4 modulate_interp;
#endif
#ifdef MODULATE_USED #ifdef MODULATE_USED
uniform mediump vec4 final_modulate; uniform mediump vec4 final_modulate;
#endif #endif
@ -177,6 +193,23 @@ VERTEX_SHADER_CODE
pixel_size_interp = abs(dst_rect.zw) * vertex; pixel_size_interp = abs(dst_rect.zw) * vertex;
#endif #endif
#ifdef USE_ATTRIB_MODULATE
// modulate doesn't need interpolating but we need to send it to the fragment shader
modulate_interp = modulate_attrib;
#endif
#ifdef USE_ATTRIB_LARGE_VERTEX
// transform is in attributes
vec2 temp;
temp = outvec.xy;
temp.x = (outvec.x * basis_attrib.x) + (outvec.y * basis_attrib.z);
temp.y = (outvec.x * basis_attrib.y) + (outvec.y * basis_attrib.w);
temp += translate_attrib;
outvec.xy = temp;
#endif
#if !defined(SKIP_TRANSFORM_USED) #if !defined(SKIP_TRANSFORM_USED)
outvec = extra_matrix * outvec; outvec = extra_matrix * outvec;
outvec = modelview_matrix * outvec; outvec = modelview_matrix * outvec;
@ -253,7 +286,7 @@ VERTEX_SHADER_CODE
pos = outvec.xy; pos = outvec.xy;
#endif #endif
#ifdef USE_LIGHT_ANGLE #ifdef USE_ATTRIB_LIGHT_ANGLE
// we add a fixed offset because we are using the sign later, // we add a fixed offset because we are using the sign later,
// and don't want floating point error around 0.0 // and don't want floating point error around 0.0
float la = abs(light_angle) - 1.0; float la = abs(light_angle) - 1.0;
@ -294,6 +327,10 @@ uniform mediump sampler2D normal_texture; // texunit:1
in highp vec2 uv_interp; in highp vec2 uv_interp;
in mediump vec4 color_interp; in mediump vec4 color_interp;
#ifdef USE_ATTRIB_MODULATE
in mediump vec4 modulate_interp;
#endif
#if defined(SCREEN_TEXTURE_USED) #if defined(SCREEN_TEXTURE_USED)
uniform sampler2D screen_texture; // texunit:-3 uniform sampler2D screen_texture; // texunit:-3
@ -410,16 +447,14 @@ uniform bool np_draw_center;
// left top right bottom in pixel coordinates // left top right bottom in pixel coordinates
uniform vec4 np_margins; uniform vec4 np_margins;
float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, float s_ratio, int np_repeat, inout int draw_center) { float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, float margin_begin, float margin_end, int np_repeat, inout int draw_center) {
float tex_size = 1.0 / tex_pixel_size; float tex_size = 1.0 / tex_pixel_size;
float screen_margin_begin = margin_begin / s_ratio; if (pixel < margin_begin) {
float screen_margin_end = margin_end / s_ratio; return pixel * tex_pixel_size;
if (pixel < screen_margin_begin) { } else if (pixel >= draw_size - margin_end) {
return pixel * s_ratio * tex_pixel_size; return (tex_size - (draw_size - pixel)) * tex_pixel_size;
} else if (pixel >= draw_size - screen_margin_end) {
return (tex_size - (draw_size - pixel) * s_ratio) * tex_pixel_size;
} else { } else {
if (!np_draw_center) { if (!np_draw_center) {
draw_center--; draw_center--;
@ -428,21 +463,21 @@ float map_ninepatch_axis(float pixel, float draw_size, float tex_pixel_size, flo
// np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum. // np_repeat is passed as uniform using NinePatchRect::AxisStretchMode enum.
if (np_repeat == 0) { // Stretch. if (np_repeat == 0) { // Stretch.
// Convert to ratio. // Convert to ratio.
float ratio = (pixel - screen_margin_begin) / (draw_size - screen_margin_begin - screen_margin_end); float ratio = (pixel - margin_begin) / (draw_size - margin_begin - margin_end);
// Scale to source texture. // Scale to source texture.
return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size; return (margin_begin + ratio * (tex_size - margin_begin - margin_end)) * tex_pixel_size;
} else if (np_repeat == 1) { // Tile. } else if (np_repeat == 1) { // Tile.
// Convert to offset. // Convert to offset.
float ofs = mod((pixel - screen_margin_begin), tex_size - margin_begin - margin_end); float ofs = mod((pixel - margin_begin), tex_size - margin_begin - margin_end);
// Scale to source texture. // Scale to source texture.
return (margin_begin + ofs) * tex_pixel_size; return (margin_begin + ofs) * tex_pixel_size;
} else if (np_repeat == 2) { // Tile Fit. } else if (np_repeat == 2) { // Tile Fit.
// Calculate scale. // Calculate scale.
float src_area = draw_size - screen_margin_begin - screen_margin_end; float src_area = draw_size - margin_begin - margin_end;
float dst_area = tex_size - margin_begin - margin_end; float dst_area = tex_size - margin_begin - margin_end;
float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5)); float scale = max(1.0, floor(src_area / max(dst_area, 0.0000001) + 0.5));
// Convert to ratio. // Convert to ratio.
float ratio = (pixel - screen_margin_begin) / src_area; float ratio = (pixel - margin_begin) / src_area;
ratio = mod(ratio * scale, 1.0); ratio = mod(ratio * scale, 1.0);
// Scale to source texture. // Scale to source texture.
return (margin_begin + ratio * dst_area) * tex_pixel_size; return (margin_begin + ratio * dst_area) * tex_pixel_size;
@ -467,11 +502,9 @@ void main() {
#ifdef USE_NINEPATCH #ifdef USE_NINEPATCH
int draw_center = 2; int draw_center = 2;
float s_ratio = max((1.0 / color_texpixel_size.x) / abs(dst_rect.z), (1.0 / color_texpixel_size.y) / abs(dst_rect.w));
s_ratio = max(1.0, s_ratio);
uv = vec2( uv = vec2(
map_ninepatch_axis(pixel_size_interp.x, abs(dst_rect.z), color_texpixel_size.x, np_margins.x, np_margins.z, s_ratio, np_repeat_h, draw_center), map_ninepatch_axis(pixel_size_interp.x, abs(dst_rect.z), color_texpixel_size.x, np_margins.x, np_margins.z, np_repeat_h, draw_center),
map_ninepatch_axis(pixel_size_interp.y, abs(dst_rect.w), color_texpixel_size.y, np_margins.y, np_margins.w, s_ratio, np_repeat_v, draw_center)); map_ninepatch_axis(pixel_size_interp.y, abs(dst_rect.w), color_texpixel_size.y, np_margins.y, np_margins.w, np_repeat_v, draw_center));
if (draw_center == 0) { if (draw_center == 0) {
color.a = 0.0; color.a = 0.0;
@ -549,6 +582,11 @@ FRAGMENT_SHADER_CODE
color *= final_modulate; color *= final_modulate;
#endif #endif
#ifdef USE_ATTRIB_MODULATE
// todo .. this won't be used at the same time as MODULATE_USED
color *= modulate_interp;
#endif
#ifdef USE_LIGHTING #ifdef USE_LIGHTING
vec2 light_vec = transformed_light_uv; vec2 light_vec = transformed_light_uv;

View file

@ -0,0 +1,5 @@
#!/usr/bin/env python
Import("env")
env.add_source_files(env.drivers_sources, "*.cpp")

View file

@ -0,0 +1,148 @@
String get_command_type_string(const RasterizerCanvas::Item::Command &p_command) const {
String sz = "";
switch (p_command.type) {
default:
break;
case RasterizerCanvas::Item::Command::TYPE_LINE: {
sz = "l";
} break;
case RasterizerCanvas::Item::Command::TYPE_POLYLINE: {
sz = "PL";
} break;
case RasterizerCanvas::Item::Command::TYPE_RECT: {
sz = "r";
} break;
case RasterizerCanvas::Item::Command::TYPE_NINEPATCH: {
sz = "n";
} break;
case RasterizerCanvas::Item::Command::TYPE_PRIMITIVE: {
sz = "PR";
} break;
case RasterizerCanvas::Item::Command::TYPE_POLYGON: {
sz = "p";
} break;
case RasterizerCanvas::Item::Command::TYPE_MESH: {
sz = "m";
} break;
case RasterizerCanvas::Item::Command::TYPE_MULTIMESH: {
sz = "MM";
} break;
case RasterizerCanvas::Item::Command::TYPE_PARTICLES: {
sz = "PA";
} break;
case RasterizerCanvas::Item::Command::TYPE_CIRCLE: {
sz = "c";
} break;
case RasterizerCanvas::Item::Command::TYPE_TRANSFORM: {
sz = "t";
// add a bit more info in debug build
const RasterizerCanvas::Item::CommandTransform *transform = static_cast<const RasterizerCanvas::Item::CommandTransform *>(&p_command);
const Transform2D &mat = transform->xform;
sz += " ";
sz += String(Variant(mat.elements[2]));
sz += " ";
} break;
case RasterizerCanvas::Item::Command::TYPE_CLIP_IGNORE: {
sz = "CI";
} break;
} // switch
return sz;
}
void diagnose_batches(RasterizerCanvas::Item::Command *const *p_commands) {
int num_batches = bdata.batches.size();
BatchColor curr_color;
curr_color.set(Color(-1, -1, -1, -1));
bool first_color_change = true;
for (int batch_num = 0; batch_num < num_batches; batch_num++) {
const Batch &batch = bdata.batches[batch_num];
bdata.frame_string += "\t\t\tbatch ";
switch (batch.type) {
case RasterizerStorageCommon::BT_POLY: {
bdata.frame_string += "P ";
bdata.frame_string += itos(batch.first_command) + "-";
bdata.frame_string += itos(batch.num_commands);
bdata.frame_string += " " + batch.color.to_string();
if (batch.num_commands > 1) {
bdata.frame_string += " MULTI";
}
if (curr_color != batch.color) {
curr_color = batch.color;
if (!first_color_change) {
bdata.frame_string += " color";
} else {
first_color_change = false;
}
}
bdata.frame_string += "\n";
} break;
case RasterizerStorageCommon::BT_LINE:
case RasterizerStorageCommon::BT_LINE_AA: {
bdata.frame_string += "L ";
bdata.frame_string += itos(batch.first_command) + "-";
bdata.frame_string += itos(batch.num_commands);
bdata.frame_string += " " + batch.color.to_string();
if (batch.num_commands > 1) {
bdata.frame_string += " MULTI";
}
if (curr_color != batch.color) {
curr_color = batch.color;
if (!first_color_change) {
bdata.frame_string += " color";
} else {
first_color_change = false;
}
}
bdata.frame_string += "\n";
} break;
case RasterizerStorageCommon::BT_RECT: {
bdata.frame_string += "R ";
bdata.frame_string += itos(batch.first_command) + "-";
bdata.frame_string += itos(batch.num_commands);
int tex_id = (int)bdata.batch_textures[batch.batch_texture_id].RID_texture.get_id();
bdata.frame_string += " [" + itos(batch.batch_texture_id) + " - " + itos(tex_id) + "]";
bdata.frame_string += " " + batch.color.to_string();
if (batch.num_commands > 1) {
bdata.frame_string += " MULTI";
}
if (curr_color != batch.color) {
curr_color = batch.color;
if (!first_color_change) {
bdata.frame_string += " color";
} else {
first_color_change = false;
}
}
bdata.frame_string += "\n";
} break;
default: {
bdata.frame_string += "D ";
bdata.frame_string += itos(batch.first_command) + "-";
bdata.frame_string += itos(batch.num_commands) + " ";
int num_show = MIN(batch.num_commands, 16);
for (int n = 0; n < num_show; n++) {
const RasterizerCanvas::Item::Command &comm = *p_commands[batch.first_command + n];
bdata.frame_string += get_command_type_string(comm) + " ";
}
bdata.frame_string += "\n";
} break;
}
}
}

View file

@ -1,5 +1,5 @@
/*************************************************************************/ /*************************************************************************/
/* rasterizer_array_gles2.h */ /* rasterizer_array.h */
/*************************************************************************/ /*************************************************************************/
/* This file is part of: */ /* This file is part of: */
/* GODOT ENGINE */ /* GODOT ENGINE */
@ -30,36 +30,6 @@
#pragma once #pragma once
/*************************************************************************/
/* rasterizer_array_gles2.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
/** /**
* Fast single-threaded growable array for POD types. * Fast single-threaded growable array for POD types.
* For use in render drivers, not for general use. * For use in render drivers, not for general use.
@ -141,14 +111,14 @@ private:
}; };
template <class T> template <class T>
class RasterizerArrayGLES2 { class RasterizerArray {
public: public:
RasterizerArrayGLES2() { RasterizerArray() {
_list = 0; _list = 0;
_size = 0; _size = 0;
_max_size = 0; _max_size = 0;
} }
~RasterizerArrayGLES2() { free(); } ~RasterizerArray() { free(); }
T &operator[](unsigned int ui) { return _list[ui]; } T &operator[](unsigned int ui) { return _list[ui]; }
const T &operator[](unsigned int ui) const { return _list[ui]; } const T &operator[](unsigned int ui) const { return _list[ui]; }
@ -208,7 +178,7 @@ public:
int max_size() const { return _max_size; } int max_size() const { return _max_size; }
const T *get_data() const { return _list; } const T *get_data() const { return _list; }
bool copy_from(const RasterizerArrayGLES2<T> &o) { bool copy_from(const RasterizerArray<T> &o) {
// no resizing done here, it should be done manually // no resizing done here, it should be done manually
if (o.size() > _max_size) if (o.size() > _max_size)
return false; return false;
@ -247,9 +217,9 @@ private:
}; };
template <class T> template <class T>
class RasterizerArray_non_pod_GLES2 { class RasterizerArray_non_pod {
public: public:
RasterizerArray_non_pod_GLES2() { RasterizerArray_non_pod() {
_size = 0; _size = 0;
} }
@ -287,3 +257,72 @@ private:
Vector<T> _list; Vector<T> _list;
int _size; int _size;
}; };
// very simple non-growable array, that keeps track of the size of a 'unit'
// which can be cast to whatever vertex format FVF required, and is initially
// created with enough memory to hold the biggest FVF.
// This allows multiple FVFs to use the same array.
class RasterizerUnitArray {
public:
RasterizerUnitArray() {
_list = nullptr;
free();
}
~RasterizerUnitArray() { free(); }
uint8_t *get_unit(unsigned int ui) { return &_list[ui * _unit_size_bytes]; }
const uint8_t *get_unit(unsigned int ui) const { return &_list[ui * _unit_size_bytes]; }
int size() const { return _size; }
int max_size() const { return _max_size; }
void free() {
if (_list) {
memdelete_arr(_list);
_list = 0;
}
_size = 0;
_max_size = 0;
_max_size_bytes = 0;
_unit_size_bytes = 0;
}
void create(int p_max_size_units, int p_max_unit_size_bytes) {
free();
_max_unit_size_bytes = p_max_unit_size_bytes;
_max_size = p_max_size_units;
_max_size_bytes = p_max_size_units * p_max_unit_size_bytes;
if (_max_size_bytes) {
_list = memnew_arr(uint8_t, _max_size_bytes);
}
}
void prepare(int p_unit_size_bytes) {
_unit_size_bytes = p_unit_size_bytes;
_size = 0;
}
// several items at a time
uint8_t *request(int p_num_items = 1) {
int old_size = _size;
_size += p_num_items;
if (_size <= _max_size) {
return get_unit(old_size);
}
// revert
_size = old_size;
return nullptr;
}
private:
uint8_t *_list;
int _size; // in units
int _max_size; // in units
int _max_size_bytes;
int _unit_size_bytes;
int _max_unit_size_bytes;
};

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,71 @@
/*************************************************************************/
/* rasterizer_storage_common.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* 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. */
/*************************************************************************/
#pragma once
class RasterizerStorageCommon {
public:
enum FVF {
FVF_UNBATCHED,
FVF_REGULAR,
FVF_COLOR,
FVF_LIGHT_ANGLE,
FVF_MODULATED,
FVF_LARGE,
};
// these flags are specifically for batching
// some of the logic is thus in rasterizer_storage.cpp
// we could alternatively set bitflags for each 'uses' and test on the fly
enum BatchFlags {
PREVENT_COLOR_BAKING = 1 << 0,
PREVENT_VERTEX_BAKING = 1 << 1,
USE_MODULATE_FVF = 1 << 2,
USE_LARGE_FVF = 1 << 3,
};
enum BatchType : uint16_t {
BT_DEFAULT = 0,
BT_RECT = 1,
BT_LINE = 2,
BT_LINE_AA = 3,
BT_POLY = 4,
BT_DUMMY = 5, // dummy batch is just used to keep the batch creation loop simple
};
enum BatchTypeFlags {
BTF_DEFAULT = 1 << BT_DEFAULT,
BTF_RECT = 1 << BT_RECT,
BTF_LINE = 1 << BT_LINE,
BTF_LINE_AA = 1 << BT_LINE_AA,
BTF_POLY = 1 << BT_POLY,
};
};

View file

@ -53,6 +53,8 @@
#include "scene/main/viewport.h" #include "scene/main/viewport.h"
#include "scene/resources/packed_scene.h" #include "scene/resources/packed_scene.h"
#include <stdlib.h>
// Min and Max are power of two in order to play nicely with successive increment. // Min and Max are power of two in order to play nicely with successive increment.
// That way, we can naturally reach a 100% zoom from boundaries. // That way, we can naturally reach a 100% zoom from boundaries.
#define MIN_ZOOM 1. / 128 #define MIN_ZOOM 1. / 128

View file

@ -116,6 +116,7 @@ def configure(env):
env.Prepend(CCFLAGS=["-g2"]) env.Prepend(CCFLAGS=["-g2"])
elif env["target"] == "debug": elif env["target"] == "debug":
env.Prepend(CCFLAGS=["-ggdb"])
env.Prepend(CCFLAGS=["-g3"]) env.Prepend(CCFLAGS=["-g3"])
env.Prepend(CPPDEFINES=["DEBUG_ENABLED"]) env.Prepend(CPPDEFINES=["DEBUG_ENABLED"])
env.Append(LINKFLAGS=["-rdynamic"]) env.Append(LINKFLAGS=["-rdynamic"])

View file

@ -1180,6 +1180,8 @@ public:
virtual bool is_low_end() const = 0; virtual bool is_low_end() const = 0;
virtual const char *gl_check_for_error(bool p_print_error = true) = 0;
virtual ~Rasterizer() {} virtual ~Rasterizer() {}
}; };

View file

@ -2439,6 +2439,10 @@ VisualServer::VisualServer() {
GLOBAL_DEF(sz_balance_render_tree, 0.17f); GLOBAL_DEF(sz_balance_render_tree, 0.17f);
ProjectSettings::get_singleton()->set_custom_property_info(sz_balance_render_tree, PropertyInfo(Variant::REAL, sz_balance_render_tree, PROPERTY_HINT_RANGE, "0.0,1.0,0.01")); ProjectSettings::get_singleton()->set_custom_property_info(sz_balance_render_tree, PropertyInfo(Variant::REAL, sz_balance_render_tree, PROPERTY_HINT_RANGE, "0.0,1.0,0.01"));
GLOBAL_DEF("rendering/quality/2d/use_software_skinning", true);
GLOBAL_DEF("rendering/quality/2d/ninepatch_mode", 0);
ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/2d/ninepatch_mode", PropertyInfo(Variant::INT, "rendering/quality/2d/ninepatch_mode", PROPERTY_HINT_ENUM, "Default,Scaling"));
GLOBAL_DEF("rendering/batching/options/use_batching", true); GLOBAL_DEF("rendering/batching/options/use_batching", true);
GLOBAL_DEF_RST("rendering/batching/options/use_batching_in_editor", true); GLOBAL_DEF_RST("rendering/batching/options/use_batching_in_editor", true);
GLOBAL_DEF("rendering/batching/options/single_rect_fallback", false); GLOBAL_DEF("rendering/batching/options/single_rect_fallback", false);